dataprovider.go 116 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367
  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/hex"
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "hash"
  19. "io"
  20. "net"
  21. "net/http"
  22. "net/url"
  23. "os"
  24. "os/exec"
  25. "path"
  26. "path/filepath"
  27. "regexp"
  28. "runtime"
  29. "strconv"
  30. "strings"
  31. "sync"
  32. "sync/atomic"
  33. "time"
  34. "github.com/GehirnInc/crypt"
  35. "github.com/GehirnInc/crypt/apr1_crypt"
  36. "github.com/GehirnInc/crypt/md5_crypt"
  37. "github.com/GehirnInc/crypt/sha512_crypt"
  38. "github.com/alexedwards/argon2id"
  39. "github.com/go-chi/render"
  40. "github.com/rs/xid"
  41. "github.com/sftpgo/sdk"
  42. passwordvalidator "github.com/wagslane/go-password-validator"
  43. "golang.org/x/crypto/bcrypt"
  44. "golang.org/x/crypto/pbkdf2"
  45. "golang.org/x/crypto/ssh"
  46. "github.com/drakkan/sftpgo/v2/httpclient"
  47. "github.com/drakkan/sftpgo/v2/kms"
  48. "github.com/drakkan/sftpgo/v2/logger"
  49. "github.com/drakkan/sftpgo/v2/mfa"
  50. "github.com/drakkan/sftpgo/v2/plugin"
  51. "github.com/drakkan/sftpgo/v2/util"
  52. "github.com/drakkan/sftpgo/v2/vfs"
  53. )
  54. const (
  55. // SQLiteDataProviderName defines the name for SQLite database provider
  56. SQLiteDataProviderName = "sqlite"
  57. // PGSQLDataProviderName defines the name for PostgreSQL database provider
  58. PGSQLDataProviderName = "postgresql"
  59. // MySQLDataProviderName defines the name for MySQL database provider
  60. MySQLDataProviderName = "mysql"
  61. // BoltDataProviderName defines the name for bbolt key/value store provider
  62. BoltDataProviderName = "bolt"
  63. // MemoryDataProviderName defines the name for memory provider
  64. MemoryDataProviderName = "memory"
  65. // CockroachDataProviderName defines the for CockroachDB provider
  66. CockroachDataProviderName = "cockroachdb"
  67. // DumpVersion defines the version for the dump.
  68. // For restore/load we support the current version and the previous one
  69. DumpVersion = 11
  70. argonPwdPrefix = "$argon2id$"
  71. bcryptPwdPrefix = "$2a$"
  72. pbkdf2SHA1Prefix = "$pbkdf2-sha1$"
  73. pbkdf2SHA256Prefix = "$pbkdf2-sha256$"
  74. pbkdf2SHA512Prefix = "$pbkdf2-sha512$"
  75. pbkdf2SHA256B64SaltPrefix = "$pbkdf2-b64salt-sha256$"
  76. md5cryptPwdPrefix = "$1$"
  77. md5cryptApr1PwdPrefix = "$apr1$"
  78. sha512cryptPwdPrefix = "$6$"
  79. trackQuotaDisabledError = "please enable track_quota in your configuration to use this method"
  80. operationAdd = "add"
  81. operationUpdate = "update"
  82. operationDelete = "delete"
  83. sqlPrefixValidChars = "abcdefghijklmnopqrstuvwxyz_0123456789"
  84. maxHookResponseSize = 1048576 // 1MB
  85. )
  86. // Supported algorithms for hashing passwords.
  87. // These algorithms can be used when SFTPGo hashes a plain text password
  88. const (
  89. HashingAlgoBcrypt = "bcrypt"
  90. HashingAlgoArgon2ID = "argon2id"
  91. )
  92. // ordering constants
  93. const (
  94. OrderASC = "ASC"
  95. OrderDESC = "DESC"
  96. )
  97. const (
  98. protocolSSH = "SSH"
  99. protocolFTP = "FTP"
  100. protocolWebDAV = "DAV"
  101. protocolHTTP = "HTTP"
  102. )
  103. var (
  104. // SupportedProviders defines the supported data providers
  105. SupportedProviders = []string{SQLiteDataProviderName, PGSQLDataProviderName, MySQLDataProviderName,
  106. BoltDataProviderName, MemoryDataProviderName, CockroachDataProviderName}
  107. // ValidPerms defines all the valid permissions for a user
  108. ValidPerms = []string{PermAny, PermListItems, PermDownload, PermUpload, PermOverwrite, PermCreateDirs, PermRename,
  109. PermRenameFiles, PermRenameDirs, PermDelete, PermDeleteFiles, PermDeleteDirs, PermCreateSymlinks, PermChmod,
  110. PermChown, PermChtimes}
  111. // ValidLoginMethods defines all the valid login methods
  112. ValidLoginMethods = []string{SSHLoginMethodPublicKey, LoginMethodPassword, SSHLoginMethodPassword,
  113. SSHLoginMethodKeyboardInteractive, SSHLoginMethodKeyAndPassword, SSHLoginMethodKeyAndKeyboardInt,
  114. LoginMethodTLSCertificate, LoginMethodTLSCertificateAndPwd}
  115. // SSHMultiStepsLoginMethods defines the supported Multi-Step Authentications
  116. SSHMultiStepsLoginMethods = []string{SSHLoginMethodKeyAndPassword, SSHLoginMethodKeyAndKeyboardInt}
  117. // ErrNoAuthTryed defines the error for connection closed before authentication
  118. ErrNoAuthTryed = errors.New("no auth tryed")
  119. // ErrNotImplemented defines the error for features not supported for a particular data provider
  120. ErrNotImplemented = errors.New("feature not supported with the configured data provider")
  121. // ValidProtocols defines all the valid protcols
  122. ValidProtocols = []string{protocolSSH, protocolFTP, protocolWebDAV, protocolHTTP}
  123. // MFAProtocols defines the supported protocols for multi-factor authentication
  124. MFAProtocols = []string{protocolHTTP, protocolSSH, protocolFTP}
  125. // ErrNoInitRequired defines the error returned by InitProvider if no inizialization/update is required
  126. ErrNoInitRequired = errors.New("the data provider is up to date")
  127. // ErrInvalidCredentials defines the error to return if the supplied credentials are invalid
  128. ErrInvalidCredentials = errors.New("invalid credentials")
  129. // ErrLoginNotAllowedFromIP defines the error to return if login is denied from the current IP
  130. ErrLoginNotAllowedFromIP = errors.New("login is not allowed from this IP")
  131. isAdminCreated = int32(0)
  132. validTLSUsernames = []string{string(sdk.TLSUsernameNone), string(sdk.TLSUsernameCN)}
  133. config Config
  134. provider Provider
  135. sqlPlaceholders []string
  136. internalHashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix}
  137. hashPwdPrefixes = []string{argonPwdPrefix, bcryptPwdPrefix, pbkdf2SHA1Prefix, pbkdf2SHA256Prefix,
  138. pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix, md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha512cryptPwdPrefix}
  139. pbkdfPwdPrefixes = []string{pbkdf2SHA1Prefix, pbkdf2SHA256Prefix, pbkdf2SHA512Prefix, pbkdf2SHA256B64SaltPrefix}
  140. pbkdfPwdB64SaltPrefixes = []string{pbkdf2SHA256B64SaltPrefix}
  141. unixPwdPrefixes = []string{md5cryptPwdPrefix, md5cryptApr1PwdPrefix, sha512cryptPwdPrefix}
  142. sharedProviders = []string{PGSQLDataProviderName, MySQLDataProviderName, CockroachDataProviderName}
  143. logSender = "dataprovider"
  144. credentialsDirPath string
  145. sqlTableUsers = "users"
  146. sqlTableFolders = "folders"
  147. sqlTableFoldersMapping = "folders_mapping"
  148. sqlTableAdmins = "admins"
  149. sqlTableAPIKeys = "api_keys"
  150. sqlTableShares = "shares"
  151. sqlTableDefenderHosts = "defender_hosts"
  152. sqlTableDefenderEvents = "defender_events"
  153. sqlTableActiveTransfers = "active_transfers"
  154. sqlTableSchemaVersion = "schema_version"
  155. argon2Params *argon2id.Params
  156. lastLoginMinDelay = 10 * time.Minute
  157. usernameRegex = regexp.MustCompile("^[a-zA-Z0-9-_.~]+$")
  158. tempPath string
  159. )
  160. type schemaVersion struct {
  161. Version int
  162. }
  163. // BcryptOptions defines the options for bcrypt password hashing
  164. type BcryptOptions struct {
  165. Cost int `json:"cost" mapstructure:"cost"`
  166. }
  167. // Argon2Options defines the options for argon2 password hashing
  168. type Argon2Options struct {
  169. Memory uint32 `json:"memory" mapstructure:"memory"`
  170. Iterations uint32 `json:"iterations" mapstructure:"iterations"`
  171. Parallelism uint8 `json:"parallelism" mapstructure:"parallelism"`
  172. }
  173. // PasswordHashing defines the configuration for password hashing
  174. type PasswordHashing struct {
  175. BcryptOptions BcryptOptions `json:"bcrypt_options" mapstructure:"bcrypt_options"`
  176. Argon2Options Argon2Options `json:"argon2_options" mapstructure:"argon2_options"`
  177. // Algorithm to use for hashing passwords. Available algorithms: argon2id, bcrypt. Default: bcrypt
  178. Algo string `json:"algo" mapstructure:"algo"`
  179. }
  180. // PasswordValidationRules defines the password validation rules
  181. type PasswordValidationRules struct {
  182. // MinEntropy defines the minimum password entropy.
  183. // 0 means disabled, any password will be accepted.
  184. // Take a look at the following link for more details
  185. // https://github.com/wagslane/go-password-validator#what-entropy-value-should-i-use
  186. MinEntropy float64 `json:"min_entropy" mapstructure:"min_entropy"`
  187. }
  188. // PasswordValidation defines the password validation rules for admins and protocol users
  189. type PasswordValidation struct {
  190. // Password validation rules for SFTPGo admin users
  191. Admins PasswordValidationRules `json:"admins" mapstructure:"admins"`
  192. // Password validation rules for SFTPGo protocol users
  193. Users PasswordValidationRules `json:"users" mapstructure:"users"`
  194. }
  195. // ObjectsActions defines the action to execute on user create, update, delete for the specified objects
  196. type ObjectsActions struct {
  197. // Valid values are add, update, delete. Empty slice to disable
  198. ExecuteOn []string `json:"execute_on" mapstructure:"execute_on"`
  199. // Valid values are user, admin, api_key
  200. ExecuteFor []string `json:"execute_for" mapstructure:"execute_for"`
  201. // Absolute path to an external program or an HTTP URL
  202. Hook string `json:"hook" mapstructure:"hook"`
  203. }
  204. // ProviderStatus defines the provider status
  205. type ProviderStatus struct {
  206. Driver string `json:"driver"`
  207. IsActive bool `json:"is_active"`
  208. Error string `json:"error"`
  209. }
  210. // AutoBackup defines the settings for automatic provider backups.
  211. // Example: hour "0" and day_of_week "*" means a backup every day at midnight.
  212. // The backup file name is in the format backup_<day_of_week>_<hour>.json
  213. // files with the same name will be overwritten
  214. type AutoBackup struct {
  215. Enabled bool `json:"enabled" mapstructure:"enabled"`
  216. // hour as standard cron expression. Allowed values: 0-23.
  217. // Allowed special characters: asterisk (*), slash (/), comma (,), hyphen (-).
  218. // More info about special characters here:
  219. // https://pkg.go.dev/github.com/robfig/cron#hdr-Special_Characters
  220. Hour string `json:"hour" mapstructure:"hour"`
  221. // Day of the week as cron expression. Allowed values: 0-6 (Sunday to Saturday).
  222. // Allowed special characters: asterisk (*), slash (/), comma (,), hyphen (-), question mark (?).
  223. // More info about special characters here:
  224. // https://pkg.go.dev/github.com/robfig/cron#hdr-Special_Characters
  225. DayOfWeek string `json:"day_of_week" mapstructure:"day_of_week"`
  226. }
  227. // Config provider configuration
  228. type Config struct {
  229. // Driver name, must be one of the SupportedProviders
  230. Driver string `json:"driver" mapstructure:"driver"`
  231. // Database name. For driver sqlite this can be the database name relative to the config dir
  232. // or the absolute path to the SQLite database.
  233. Name string `json:"name" mapstructure:"name"`
  234. // Database host
  235. Host string `json:"host" mapstructure:"host"`
  236. // Database port
  237. Port int `json:"port" mapstructure:"port"`
  238. // Database username
  239. Username string `json:"username" mapstructure:"username"`
  240. // Database password
  241. Password string `json:"password" mapstructure:"password"`
  242. // Used for drivers mysql and postgresql.
  243. // 0 disable SSL/TLS connections.
  244. // 1 require ssl.
  245. // 2 set ssl mode to verify-ca for driver postgresql and skip-verify for driver mysql.
  246. // 3 set ssl mode to verify-full for driver postgresql and preferred for driver mysql.
  247. SSLMode int `json:"sslmode" mapstructure:"sslmode"`
  248. // Path to the root certificate authority used to verify that the server certificate was signed by a trusted CA
  249. RootCert string `json:"root_cert" mapstructure:"root_cert"`
  250. // Path to the client certificate for two-way TLS authentication
  251. ClientCert string `json:"client_cert" mapstructure:"client_cert"`
  252. // Path to the client key for two-way TLS authentication
  253. ClientKey string `json:"client_key" mapstructure:"client_key"`
  254. // Custom database connection string.
  255. // If not empty this connection string will be used instead of build one using the previous parameters
  256. ConnectionString string `json:"connection_string" mapstructure:"connection_string"`
  257. // prefix for SQL tables
  258. SQLTablesPrefix string `json:"sql_tables_prefix" mapstructure:"sql_tables_prefix"`
  259. // Set the preferred way to track users quota between the following choices:
  260. // 0, disable quota tracking. REST API to scan user dir and update quota will do nothing
  261. // 1, quota is updated each time a user upload or delete a file even if the user has no quota restrictions
  262. // 2, quota is updated each time a user upload or delete a file but only for users with quota restrictions
  263. // and for virtual folders.
  264. // With this configuration the "quota scan" REST API can still be used to periodically update space usage
  265. // for users without quota restrictions
  266. TrackQuota int `json:"track_quota" mapstructure:"track_quota"`
  267. // Sets the maximum number of open connections for mysql and postgresql driver.
  268. // Default 0 (unlimited)
  269. PoolSize int `json:"pool_size" mapstructure:"pool_size"`
  270. // Users default base directory.
  271. // If no home dir is defined while adding a new user, and this value is
  272. // a valid absolute path, then the user home dir will be automatically
  273. // defined as the path obtained joining the base dir and the username
  274. UsersBaseDir string `json:"users_base_dir" mapstructure:"users_base_dir"`
  275. // Actions to execute on objects add, update, delete.
  276. // The supported objects are user, admin, api_key.
  277. // Update action will not be fired for internal updates such as the last login or the user quota fields.
  278. Actions ObjectsActions `json:"actions" mapstructure:"actions"`
  279. // Absolute path to an external program or an HTTP URL to invoke for users authentication.
  280. // Leave empty to use builtin authentication.
  281. // If the authentication succeed the user will be automatically added/updated inside the defined data provider.
  282. // Actions defined for user added/updated will not be executed in this case.
  283. // This method is slower than built-in authentication methods, but it's very flexible as anyone can
  284. // easily write his own authentication hooks.
  285. ExternalAuthHook string `json:"external_auth_hook" mapstructure:"external_auth_hook"`
  286. // ExternalAuthScope defines the scope for the external authentication hook.
  287. // - 0 means all supported authentication scopes, the external hook will be executed for password,
  288. // public key, keyboard interactive authentication and TLS certificates
  289. // - 1 means passwords only
  290. // - 2 means public keys only
  291. // - 4 means keyboard interactive only
  292. // - 8 means TLS certificates only
  293. // you can combine the scopes, for example 3 means password and public key, 5 password and keyboard
  294. // interactive and so on
  295. ExternalAuthScope int `json:"external_auth_scope" mapstructure:"external_auth_scope"`
  296. // CredentialsPath defines the directory for storing user provided credential files such as
  297. // Google Cloud Storage credentials. It can be a path relative to the config dir or an
  298. // absolute path
  299. CredentialsPath string `json:"credentials_path" mapstructure:"credentials_path"`
  300. // Absolute path to an external program or an HTTP URL to invoke just before the user login.
  301. // This program/URL allows to modify or create the user trying to login.
  302. // It is useful if you have users with dynamic fields to update just before the login.
  303. // Please note that if you want to create a new user, the pre-login hook response must
  304. // include all the mandatory user fields.
  305. //
  306. // The pre-login hook must finish within 30 seconds.
  307. //
  308. // If an error happens while executing the "PreLoginHook" then login will be denied.
  309. // PreLoginHook and ExternalAuthHook are mutally exclusive.
  310. // Leave empty to disable.
  311. PreLoginHook string `json:"pre_login_hook" mapstructure:"pre_login_hook"`
  312. // Absolute path to an external program or an HTTP URL to invoke after the user login.
  313. // Based on the configured scope you can choose if notify failed or successful logins
  314. // or both
  315. PostLoginHook string `json:"post_login_hook" mapstructure:"post_login_hook"`
  316. // PostLoginScope defines the scope for the post-login hook.
  317. // - 0 means notify both failed and successful logins
  318. // - 1 means notify failed logins
  319. // - 2 means notify successful logins
  320. PostLoginScope int `json:"post_login_scope" mapstructure:"post_login_scope"`
  321. // Absolute path to an external program or an HTTP URL to invoke just before password
  322. // authentication. This hook allows you to externally check the provided password,
  323. // its main use case is to allow to easily support things like password+OTP for protocols
  324. // without keyboard interactive support such as FTP and WebDAV. You can ask your users
  325. // to login using a string consisting of a fixed password and a One Time Token, you
  326. // can verify the token inside the hook and ask to SFTPGo to verify the fixed part.
  327. CheckPasswordHook string `json:"check_password_hook" mapstructure:"check_password_hook"`
  328. // CheckPasswordScope defines the scope for the check password hook.
  329. // - 0 means all protocols
  330. // - 1 means SSH
  331. // - 2 means FTP
  332. // - 4 means WebDAV
  333. // you can combine the scopes, for example 6 means FTP and WebDAV
  334. CheckPasswordScope int `json:"check_password_scope" mapstructure:"check_password_scope"`
  335. // Defines how the database will be initialized/updated:
  336. // - 0 means automatically
  337. // - 1 means manually using the initprovider sub-command
  338. UpdateMode int `json:"update_mode" mapstructure:"update_mode"`
  339. // PasswordHashing defines the configuration for password hashing
  340. PasswordHashing PasswordHashing `json:"password_hashing" mapstructure:"password_hashing"`
  341. // PreferDatabaseCredentials indicates whether credential files (currently used for Google
  342. // Cloud Storage) should be stored in the database instead of in the directory specified by
  343. // CredentialsPath.
  344. PreferDatabaseCredentials bool `json:"prefer_database_credentials" mapstructure:"prefer_database_credentials"`
  345. // PasswordValidation defines the password validation rules
  346. PasswordValidation PasswordValidation `json:"password_validation" mapstructure:"password_validation"`
  347. // Verifying argon2 passwords has a high memory and computational cost,
  348. // by enabling, in memory, password caching you reduce this cost.
  349. PasswordCaching bool `json:"password_caching" mapstructure:"password_caching"`
  350. // DelayedQuotaUpdate defines the number of seconds to accumulate quota updates.
  351. // If there are a lot of close uploads, accumulating quota updates can save you many
  352. // queries to the data provider.
  353. // If you want to track quotas, a scheduled quota update is recommended in any case, the stored
  354. // quota size may be incorrect for several reasons, such as an unexpected shutdown, temporary provider
  355. // failures, file copied outside of SFTPGo, and so on.
  356. // 0 means immediate quota update.
  357. DelayedQuotaUpdate int `json:"delayed_quota_update" mapstructure:"delayed_quota_update"`
  358. // If enabled, a default admin user with username "admin" and password "password" will be created
  359. // on first start.
  360. // You can also create the first admin user by using the web interface or by loading initial data.
  361. CreateDefaultAdmin bool `json:"create_default_admin" mapstructure:"create_default_admin"`
  362. // Rules for usernames and folder names:
  363. // - 0 means no rules
  364. // - 1 means you can use any UTF-8 character. The names are used in URIs for REST API and Web admin.
  365. // By default only unreserved URI characters are allowed: ALPHA / DIGIT / "-" / "." / "_" / "~".
  366. // - 2 means names are converted to lowercase before saving/matching and so case
  367. // insensitive matching is possible
  368. // - 4 means trimming trailing and leading white spaces before saving/matching
  369. // Rules can be combined, for example 3 means both converting to lowercase and allowing any UTF-8 character.
  370. // Enabling these options for existing installations could be backward incompatible, some users
  371. // could be unable to login, for example existing users with mixed cases in their usernames.
  372. // You have to ensure that all existing users respect the defined rules.
  373. NamingRules int `json:"naming_rules" mapstructure:"naming_rules"`
  374. // If the data provider is shared across multiple SFTPGo instances, set this parameter to 1.
  375. // MySQL, PostgreSQL and CockroachDB can be shared, this setting is ignored for other data
  376. // providers. For shared data providers, SFTPGo periodically reloads the latest updated users,
  377. // based on the "updated_at" field, and updates its internal caches if users are updated from
  378. // a different instance. This check, if enabled, is executed every 10 minutes.
  379. // For shared data providers, active transfers are persisted in the database and thus
  380. // quota checks between ongoing transfers will work cross multiple instances
  381. IsShared int `json:"is_shared" mapstructure:"is_shared"`
  382. // Path to the backup directory. This can be an absolute path or a path relative to the config dir
  383. BackupsPath string `json:"backups_path" mapstructure:"backups_path"`
  384. // Settings for automatic backups
  385. AutoBackup AutoBackup `json:"auto_backup" mapstructure:"auto_backup"`
  386. }
  387. // GetShared returns the provider share mode
  388. func (c *Config) GetShared() int {
  389. if !util.IsStringInSlice(c.Driver, sharedProviders) {
  390. return 0
  391. }
  392. return c.IsShared
  393. }
  394. func (c *Config) convertName(name string) string {
  395. if c.NamingRules == 0 {
  396. return name
  397. }
  398. if c.NamingRules&2 != 0 {
  399. name = strings.ToLower(name)
  400. }
  401. if c.NamingRules&4 != 0 {
  402. name = strings.TrimSpace(name)
  403. }
  404. return name
  405. }
  406. // IsDefenderSupported returns true if the configured provider supports the defender
  407. func (c *Config) IsDefenderSupported() bool {
  408. switch c.Driver {
  409. case MySQLDataProviderName, PGSQLDataProviderName, CockroachDataProviderName:
  410. return true
  411. default:
  412. return false
  413. }
  414. }
  415. func (c *Config) requireCustomTLSForMySQL() bool {
  416. if config.RootCert != "" && util.IsFileInputValid(config.RootCert) {
  417. return config.SSLMode != 0
  418. }
  419. if config.ClientCert != "" && config.ClientKey != "" && util.IsFileInputValid(config.ClientCert) &&
  420. util.IsFileInputValid(config.ClientKey) {
  421. return config.SSLMode != 0
  422. }
  423. return false
  424. }
  425. func (c *Config) doBackup() {
  426. now := time.Now()
  427. outputFile := filepath.Join(c.BackupsPath, fmt.Sprintf("backup_%v_%v.json", now.Weekday(), now.Hour()))
  428. providerLog(logger.LevelDebug, "starting auto backup to file %#v", outputFile)
  429. err := os.MkdirAll(filepath.Dir(outputFile), 0700)
  430. if err != nil {
  431. providerLog(logger.LevelError, "unable to create backup dir %#v: %v", outputFile, err)
  432. return
  433. }
  434. backup, err := DumpData()
  435. if err != nil {
  436. providerLog(logger.LevelError, "unable to execute backup: %v", err)
  437. return
  438. }
  439. dump, err := json.Marshal(backup)
  440. if err != nil {
  441. providerLog(logger.LevelError, "unable to marshal backup as JSON: %v", err)
  442. return
  443. }
  444. err = os.WriteFile(outputFile, dump, 0600)
  445. if err != nil {
  446. providerLog(logger.LevelError, "unable to save backup: %v", err)
  447. return
  448. }
  449. providerLog(logger.LevelDebug, "auto backup saved to %#v", outputFile)
  450. }
  451. // ConvertName converts the given name based on the configured rules
  452. func ConvertName(name string) string {
  453. return config.convertName(name)
  454. }
  455. // ActiveTransfer defines an active protocol transfer
  456. type ActiveTransfer struct {
  457. ID int64
  458. Type int
  459. ConnID string
  460. Username string
  461. FolderName string
  462. IP string
  463. TruncatedSize int64
  464. CurrentULSize int64
  465. CurrentDLSize int64
  466. CreatedAt int64
  467. UpdatedAt int64
  468. }
  469. // TransferQuota stores the allowed transfer quota fields
  470. type TransferQuota struct {
  471. ULSize int64
  472. DLSize int64
  473. TotalSize int64
  474. AllowedULSize int64
  475. AllowedDLSize int64
  476. AllowedTotalSize int64
  477. }
  478. // HasSizeLimits returns true if any size limit is set
  479. func (q *TransferQuota) HasSizeLimits() bool {
  480. return q.AllowedDLSize > 0 || q.AllowedULSize > 0 || q.AllowedTotalSize > 0
  481. }
  482. // HasUploadSpace returns true if there is transfer upload space available
  483. func (q *TransferQuota) HasUploadSpace() bool {
  484. if q.TotalSize <= 0 && q.ULSize <= 0 {
  485. return true
  486. }
  487. if q.TotalSize > 0 {
  488. return q.AllowedTotalSize > 0
  489. }
  490. return q.AllowedULSize > 0
  491. }
  492. // HasDownloadSpace returns true if there is transfer download space available
  493. func (q *TransferQuota) HasDownloadSpace() bool {
  494. if q.TotalSize <= 0 && q.DLSize <= 0 {
  495. return true
  496. }
  497. if q.TotalSize > 0 {
  498. return q.AllowedTotalSize > 0
  499. }
  500. return q.AllowedDLSize > 0
  501. }
  502. // DefenderEntry defines a defender entry
  503. type DefenderEntry struct {
  504. ID int64 `json:"-"`
  505. IP string `json:"ip"`
  506. Score int `json:"score,omitempty"`
  507. BanTime time.Time `json:"ban_time,omitempty"`
  508. }
  509. // GetID returns an unique ID for a defender entry
  510. func (d *DefenderEntry) GetID() string {
  511. return hex.EncodeToString([]byte(d.IP))
  512. }
  513. // GetBanTime returns the ban time for a defender entry as string
  514. func (d *DefenderEntry) GetBanTime() string {
  515. if d.BanTime.IsZero() {
  516. return ""
  517. }
  518. return d.BanTime.UTC().Format(time.RFC3339)
  519. }
  520. // MarshalJSON returns the JSON encoding of a DefenderEntry.
  521. func (d *DefenderEntry) MarshalJSON() ([]byte, error) {
  522. return json.Marshal(&struct {
  523. ID string `json:"id"`
  524. IP string `json:"ip"`
  525. Score int `json:"score,omitempty"`
  526. BanTime string `json:"ban_time,omitempty"`
  527. }{
  528. ID: d.GetID(),
  529. IP: d.IP,
  530. Score: d.Score,
  531. BanTime: d.GetBanTime(),
  532. })
  533. }
  534. // BackupData defines the structure for the backup/restore files
  535. type BackupData struct {
  536. Users []User `json:"users"`
  537. Folders []vfs.BaseVirtualFolder `json:"folders"`
  538. Admins []Admin `json:"admins"`
  539. APIKeys []APIKey `json:"api_keys"`
  540. Shares []Share `json:"shares"`
  541. Version int `json:"version"`
  542. }
  543. // HasFolder returns true if the folder with the given name is included
  544. func (d *BackupData) HasFolder(name string) bool {
  545. for _, folder := range d.Folders {
  546. if folder.Name == name {
  547. return true
  548. }
  549. }
  550. return false
  551. }
  552. type checkPasswordRequest struct {
  553. Username string `json:"username"`
  554. IP string `json:"ip"`
  555. Password string `json:"password"`
  556. Protocol string `json:"protocol"`
  557. }
  558. type checkPasswordResponse struct {
  559. // 0 KO, 1 OK, 2 partial success, -1 not executed
  560. Status int `json:"status"`
  561. // for status = 2 this is the password to check against the one stored
  562. // inside the SFTPGo data provider
  563. ToVerify string `json:"to_verify"`
  564. }
  565. // GetQuotaTracking returns the configured mode for user's quota tracking
  566. func GetQuotaTracking() int {
  567. return config.TrackQuota
  568. }
  569. // HasUsersBaseDir returns true if users base dir is set
  570. func HasUsersBaseDir() bool {
  571. return config.UsersBaseDir != ""
  572. }
  573. // Provider defines the interface that data providers must implement.
  574. type Provider interface {
  575. validateUserAndPass(username, password, ip, protocol string) (User, error)
  576. validateUserAndPubKey(username string, pubKey []byte, isSSHCert bool) (User, string, error)
  577. validateUserAndTLSCert(username, protocol string, tlsCert *x509.Certificate) (User, error)
  578. updateQuota(username string, filesAdd int, sizeAdd int64, reset bool) error
  579. updateTransferQuota(username string, uploadSize, downloadSize int64, reset bool) error
  580. getUsedQuota(username string) (int, int64, int64, int64, error)
  581. userExists(username string) (User, error)
  582. addUser(user *User) error
  583. updateUser(user *User) error
  584. deleteUser(user *User) error
  585. getUsers(limit int, offset int, order string) ([]User, error)
  586. dumpUsers() ([]User, error)
  587. getRecentlyUpdatedUsers(after int64) ([]User, error)
  588. getUsersForQuotaCheck(toFetch map[string]bool) ([]User, error)
  589. updateLastLogin(username string) error
  590. updateAdminLastLogin(username string) error
  591. setUpdatedAt(username string)
  592. getFolders(limit, offset int, order string) ([]vfs.BaseVirtualFolder, error)
  593. getFolderByName(name string) (vfs.BaseVirtualFolder, error)
  594. addFolder(folder *vfs.BaseVirtualFolder) error
  595. updateFolder(folder *vfs.BaseVirtualFolder) error
  596. deleteFolder(folder *vfs.BaseVirtualFolder) error
  597. updateFolderQuota(name string, filesAdd int, sizeAdd int64, reset bool) error
  598. getUsedFolderQuota(name string) (int, int64, error)
  599. dumpFolders() ([]vfs.BaseVirtualFolder, error)
  600. adminExists(username string) (Admin, error)
  601. addAdmin(admin *Admin) error
  602. updateAdmin(admin *Admin) error
  603. deleteAdmin(admin *Admin) error
  604. getAdmins(limit int, offset int, order string) ([]Admin, error)
  605. dumpAdmins() ([]Admin, error)
  606. validateAdminAndPass(username, password, ip string) (Admin, error)
  607. apiKeyExists(keyID string) (APIKey, error)
  608. addAPIKey(apiKey *APIKey) error
  609. updateAPIKey(apiKey *APIKey) error
  610. deleteAPIKey(apiKey *APIKey) error
  611. getAPIKeys(limit int, offset int, order string) ([]APIKey, error)
  612. dumpAPIKeys() ([]APIKey, error)
  613. updateAPIKeyLastUse(keyID string) error
  614. shareExists(shareID, username string) (Share, error)
  615. addShare(share *Share) error
  616. updateShare(share *Share) error
  617. deleteShare(share *Share) error
  618. getShares(limit int, offset int, order, username string) ([]Share, error)
  619. dumpShares() ([]Share, error)
  620. updateShareLastUse(shareID string, numTokens int) error
  621. getDefenderHosts(from int64, limit int) ([]DefenderEntry, error)
  622. getDefenderHostByIP(ip string, from int64) (DefenderEntry, error)
  623. isDefenderHostBanned(ip string) (DefenderEntry, error)
  624. updateDefenderBanTime(ip string, minutes int) error
  625. deleteDefenderHost(ip string) error
  626. addDefenderEvent(ip string, score int) error
  627. setDefenderBanTime(ip string, banTime int64) error
  628. cleanupDefender(from int64) error
  629. addActiveTransfer(transfer ActiveTransfer) error
  630. updateActiveTransferSizes(ulSize, dlSize, transferID int64, connectionID string) error
  631. removeActiveTransfer(transferID int64, connectionID string) error
  632. cleanupActiveTransfers(before time.Time) error
  633. getActiveTransfers(from time.Time) ([]ActiveTransfer, error)
  634. checkAvailability() error
  635. close() error
  636. reloadConfig() error
  637. initializeDatabase() error
  638. migrateDatabase() error
  639. revertDatabase(targetVersion int) error
  640. resetDatabase() error
  641. }
  642. // SetTempPath sets the path for temporary files
  643. func SetTempPath(fsPath string) {
  644. tempPath = fsPath
  645. }
  646. // Initialize the data provider.
  647. // An error is returned if the configured driver is invalid or if the data provider cannot be initialized
  648. func Initialize(cnf Config, basePath string, checkAdmins bool) error {
  649. var err error
  650. config = cnf
  651. cnf.BackupsPath = getConfigPath(cnf.BackupsPath, basePath)
  652. if cnf.BackupsPath == "" {
  653. return fmt.Errorf("required directory is invalid, backup path %#v", cnf.BackupsPath)
  654. }
  655. credentialsDirPath = getConfigPath(config.CredentialsPath, basePath)
  656. vfs.SetCredentialsDirPath(credentialsDirPath)
  657. if err = initializeHashingAlgo(&cnf); err != nil {
  658. return err
  659. }
  660. if err = validateHooks(); err != nil {
  661. return err
  662. }
  663. err = createProvider(basePath)
  664. if err != nil {
  665. return err
  666. }
  667. if cnf.UpdateMode == 0 {
  668. err = provider.initializeDatabase()
  669. if err != nil && err != ErrNoInitRequired {
  670. logger.WarnToConsole("Unable to initialize data provider: %v", err)
  671. providerLog(logger.LevelError, "Unable to initialize data provider: %v", err)
  672. return err
  673. }
  674. if err == nil {
  675. logger.DebugToConsole("Data provider successfully initialized")
  676. }
  677. err = provider.migrateDatabase()
  678. if err != nil && err != ErrNoInitRequired {
  679. providerLog(logger.LevelError, "database migration error: %v", err)
  680. return err
  681. }
  682. if checkAdmins && cnf.CreateDefaultAdmin {
  683. err = checkDefaultAdmin()
  684. if err != nil {
  685. providerLog(logger.LevelError, "erro checking the default admin: %v", err)
  686. return err
  687. }
  688. }
  689. } else {
  690. providerLog(logger.LevelInfo, "database initialization/migration skipped, manual mode is configured")
  691. }
  692. admins, err := provider.getAdmins(1, 0, OrderASC)
  693. if err != nil {
  694. return err
  695. }
  696. atomic.StoreInt32(&isAdminCreated, int32(len(admins)))
  697. delayedQuotaUpdater.start()
  698. return startScheduler()
  699. }
  700. func validateHooks() error {
  701. var hooks []string
  702. if config.PreLoginHook != "" && !strings.HasPrefix(config.PreLoginHook, "http") {
  703. hooks = append(hooks, config.PreLoginHook)
  704. }
  705. if config.ExternalAuthHook != "" && !strings.HasPrefix(config.ExternalAuthHook, "http") {
  706. hooks = append(hooks, config.ExternalAuthHook)
  707. }
  708. if config.PostLoginHook != "" && !strings.HasPrefix(config.PostLoginHook, "http") {
  709. hooks = append(hooks, config.PostLoginHook)
  710. }
  711. if config.CheckPasswordHook != "" && !strings.HasPrefix(config.CheckPasswordHook, "http") {
  712. hooks = append(hooks, config.CheckPasswordHook)
  713. }
  714. for _, hook := range hooks {
  715. if !filepath.IsAbs(hook) {
  716. return fmt.Errorf("invalid hook: %#v must be an absolute path", hook)
  717. }
  718. _, err := os.Stat(hook)
  719. if err != nil {
  720. providerLog(logger.LevelError, "invalid hook: %v", err)
  721. return err
  722. }
  723. }
  724. return nil
  725. }
  726. // GetBackupsPath returns the normalized backups path
  727. func GetBackupsPath() string {
  728. return config.BackupsPath
  729. }
  730. func initializeHashingAlgo(cnf *Config) error {
  731. argon2Params = &argon2id.Params{
  732. Memory: cnf.PasswordHashing.Argon2Options.Memory,
  733. Iterations: cnf.PasswordHashing.Argon2Options.Iterations,
  734. Parallelism: cnf.PasswordHashing.Argon2Options.Parallelism,
  735. SaltLength: 16,
  736. KeyLength: 32,
  737. }
  738. if config.PasswordHashing.Algo == HashingAlgoBcrypt {
  739. if config.PasswordHashing.BcryptOptions.Cost > bcrypt.MaxCost {
  740. err := fmt.Errorf("invalid bcrypt cost %v, max allowed %v", config.PasswordHashing.BcryptOptions.Cost, bcrypt.MaxCost)
  741. logger.WarnToConsole("Unable to initialize data provider: %v", err)
  742. providerLog(logger.LevelError, "Unable to initialize data provider: %v", err)
  743. return err
  744. }
  745. }
  746. return nil
  747. }
  748. func validateSQLTablesPrefix() error {
  749. if config.SQLTablesPrefix != "" {
  750. for _, char := range config.SQLTablesPrefix {
  751. if !strings.Contains(sqlPrefixValidChars, strings.ToLower(string(char))) {
  752. return errors.New("invalid sql_tables_prefix only chars in range 'a..z', 'A..Z', '0-9' and '_' are allowed")
  753. }
  754. }
  755. sqlTableUsers = config.SQLTablesPrefix + sqlTableUsers
  756. sqlTableFolders = config.SQLTablesPrefix + sqlTableFolders
  757. sqlTableFoldersMapping = config.SQLTablesPrefix + sqlTableFoldersMapping
  758. sqlTableAdmins = config.SQLTablesPrefix + sqlTableAdmins
  759. sqlTableAPIKeys = config.SQLTablesPrefix + sqlTableAPIKeys
  760. sqlTableShares = config.SQLTablesPrefix + sqlTableShares
  761. sqlTableDefenderEvents = config.SQLTablesPrefix + sqlTableDefenderEvents
  762. sqlTableDefenderHosts = config.SQLTablesPrefix + sqlTableDefenderHosts
  763. sqlTableActiveTransfers = config.SQLTablesPrefix + sqlTableActiveTransfers
  764. sqlTableSchemaVersion = config.SQLTablesPrefix + sqlTableSchemaVersion
  765. providerLog(logger.LevelDebug, "sql table for users %#v, folders %#v folders mapping %#v admins %#v "+
  766. "api keys %#v shares %#v defender hosts %#v defender events %#v transfers %#v schema version %#v",
  767. sqlTableUsers, sqlTableFolders, sqlTableFoldersMapping, sqlTableAdmins, sqlTableAPIKeys,
  768. sqlTableShares, sqlTableDefenderHosts, sqlTableDefenderEvents, sqlTableActiveTransfers, sqlTableSchemaVersion)
  769. }
  770. return nil
  771. }
  772. func checkDefaultAdmin() error {
  773. admins, err := provider.getAdmins(1, 0, OrderASC)
  774. if err != nil {
  775. return err
  776. }
  777. if len(admins) > 0 {
  778. return nil
  779. }
  780. logger.Debug(logSender, "", "no admins found, try to create the default one")
  781. // we need to create the default admin
  782. admin := &Admin{}
  783. if err := admin.setFromEnv(); err != nil {
  784. return err
  785. }
  786. return provider.addAdmin(admin)
  787. }
  788. // InitializeDatabase creates the initial database structure
  789. func InitializeDatabase(cnf Config, basePath string) error {
  790. config = cnf
  791. if filepath.IsAbs(config.CredentialsPath) {
  792. credentialsDirPath = config.CredentialsPath
  793. } else {
  794. credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
  795. }
  796. vfs.SetCredentialsDirPath(credentialsDirPath)
  797. if err := initializeHashingAlgo(&cnf); err != nil {
  798. return err
  799. }
  800. err := createProvider(basePath)
  801. if err != nil {
  802. return err
  803. }
  804. err = provider.initializeDatabase()
  805. if err != nil && err != ErrNoInitRequired {
  806. return err
  807. }
  808. return provider.migrateDatabase()
  809. }
  810. // RevertDatabase restores schema and/or data to a previous version
  811. func RevertDatabase(cnf Config, basePath string, targetVersion int) error {
  812. config = cnf
  813. if filepath.IsAbs(config.CredentialsPath) {
  814. credentialsDirPath = config.CredentialsPath
  815. } else {
  816. credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
  817. }
  818. err := createProvider(basePath)
  819. if err != nil {
  820. return err
  821. }
  822. err = provider.initializeDatabase()
  823. if err != nil && err != ErrNoInitRequired {
  824. return err
  825. }
  826. return provider.revertDatabase(targetVersion)
  827. }
  828. // ResetDatabase restores schema and/or data to a previous version
  829. func ResetDatabase(cnf Config, basePath string) error {
  830. config = cnf
  831. if filepath.IsAbs(config.CredentialsPath) {
  832. credentialsDirPath = config.CredentialsPath
  833. } else {
  834. credentialsDirPath = filepath.Join(basePath, config.CredentialsPath)
  835. }
  836. if err := createProvider(basePath); err != nil {
  837. return err
  838. }
  839. return provider.resetDatabase()
  840. }
  841. // CheckAdminAndPass validates the given admin and password connecting from ip
  842. func CheckAdminAndPass(username, password, ip string) (Admin, error) {
  843. username = config.convertName(username)
  844. return provider.validateAdminAndPass(username, password, ip)
  845. }
  846. // CheckCachedUserCredentials checks the credentials for a cached user
  847. func CheckCachedUserCredentials(user *CachedUser, password, loginMethod, protocol string, tlsCert *x509.Certificate) error {
  848. if loginMethod != LoginMethodPassword {
  849. _, err := checkUserAndTLSCertificate(&user.User, protocol, tlsCert)
  850. if err != nil {
  851. return err
  852. }
  853. if loginMethod == LoginMethodTLSCertificate {
  854. if !user.User.IsLoginMethodAllowed(LoginMethodTLSCertificate, protocol, nil) {
  855. return fmt.Errorf("certificate login method is not allowed for user %#v", user.User.Username)
  856. }
  857. return nil
  858. }
  859. }
  860. if err := user.User.CheckLoginConditions(); err != nil {
  861. return err
  862. }
  863. if password == "" {
  864. return ErrInvalidCredentials
  865. }
  866. if user.Password != "" {
  867. if password == user.Password {
  868. return nil
  869. }
  870. } else {
  871. if ok, _ := isPasswordOK(&user.User, password); ok {
  872. return nil
  873. }
  874. }
  875. return ErrInvalidCredentials
  876. }
  877. // CheckCompositeCredentials checks multiple credentials.
  878. // WebDAV users can send both a password and a TLS certificate within the same request
  879. func CheckCompositeCredentials(username, password, ip, loginMethod, protocol string, tlsCert *x509.Certificate) (User, string, error) {
  880. username = config.convertName(username)
  881. if loginMethod == LoginMethodPassword {
  882. user, err := CheckUserAndPass(username, password, ip, protocol)
  883. return user, loginMethod, err
  884. }
  885. user, err := CheckUserBeforeTLSAuth(username, ip, protocol, tlsCert)
  886. if err != nil {
  887. return user, loginMethod, err
  888. }
  889. if !user.IsTLSUsernameVerificationEnabled() {
  890. // for backward compatibility with 2.0.x we only check the password and change the login method here
  891. // in future updates we have to return an error
  892. user, err := CheckUserAndPass(username, password, ip, protocol)
  893. return user, LoginMethodPassword, err
  894. }
  895. user, err = checkUserAndTLSCertificate(&user, protocol, tlsCert)
  896. if err != nil {
  897. return user, loginMethod, err
  898. }
  899. if loginMethod == LoginMethodTLSCertificate && !user.IsLoginMethodAllowed(LoginMethodTLSCertificate, protocol, nil) {
  900. return user, loginMethod, fmt.Errorf("certificate login method is not allowed for user %#v", user.Username)
  901. }
  902. if loginMethod == LoginMethodTLSCertificateAndPwd {
  903. if plugin.Handler.HasAuthScope(plugin.AuthScopePassword) {
  904. user, err = doPluginAuth(username, password, nil, ip, protocol, nil, plugin.AuthScopePassword)
  905. } else if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&1 != 0) {
  906. user, err = doExternalAuth(username, password, nil, "", ip, protocol, nil)
  907. } else if config.PreLoginHook != "" {
  908. user, err = executePreLoginHook(username, LoginMethodPassword, ip, protocol)
  909. }
  910. if err != nil {
  911. return user, loginMethod, err
  912. }
  913. user, err = checkUserAndPass(&user, password, ip, protocol)
  914. }
  915. return user, loginMethod, err
  916. }
  917. // CheckUserBeforeTLSAuth checks if a user exits before trying mutual TLS
  918. func CheckUserBeforeTLSAuth(username, ip, protocol string, tlsCert *x509.Certificate) (User, error) {
  919. username = config.convertName(username)
  920. if plugin.Handler.HasAuthScope(plugin.AuthScopeTLSCertificate) {
  921. return doPluginAuth(username, "", nil, ip, protocol, tlsCert, plugin.AuthScopeTLSCertificate)
  922. }
  923. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&8 != 0) {
  924. return doExternalAuth(username, "", nil, "", ip, protocol, tlsCert)
  925. }
  926. if config.PreLoginHook != "" {
  927. return executePreLoginHook(username, LoginMethodTLSCertificate, ip, protocol)
  928. }
  929. return UserExists(username)
  930. }
  931. // CheckUserAndTLSCert returns the SFTPGo user with the given username and check if the
  932. // given TLS certificate allow authentication without password
  933. func CheckUserAndTLSCert(username, ip, protocol string, tlsCert *x509.Certificate) (User, error) {
  934. username = config.convertName(username)
  935. if plugin.Handler.HasAuthScope(plugin.AuthScopeTLSCertificate) {
  936. user, err := doPluginAuth(username, "", nil, ip, protocol, tlsCert, plugin.AuthScopeTLSCertificate)
  937. if err != nil {
  938. return user, err
  939. }
  940. return checkUserAndTLSCertificate(&user, protocol, tlsCert)
  941. }
  942. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&8 != 0) {
  943. user, err := doExternalAuth(username, "", nil, "", ip, protocol, tlsCert)
  944. if err != nil {
  945. return user, err
  946. }
  947. return checkUserAndTLSCertificate(&user, protocol, tlsCert)
  948. }
  949. if config.PreLoginHook != "" {
  950. user, err := executePreLoginHook(username, LoginMethodTLSCertificate, ip, protocol)
  951. if err != nil {
  952. return user, err
  953. }
  954. return checkUserAndTLSCertificate(&user, protocol, tlsCert)
  955. }
  956. return provider.validateUserAndTLSCert(username, protocol, tlsCert)
  957. }
  958. // CheckUserAndPass retrieves the SFTPGo user with the given username and password if a match is found or an error
  959. func CheckUserAndPass(username, password, ip, protocol string) (User, error) {
  960. username = config.convertName(username)
  961. if plugin.Handler.HasAuthScope(plugin.AuthScopePassword) {
  962. user, err := doPluginAuth(username, password, nil, ip, protocol, nil, plugin.AuthScopePassword)
  963. if err != nil {
  964. return user, err
  965. }
  966. return checkUserAndPass(&user, password, ip, protocol)
  967. }
  968. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&1 != 0) {
  969. user, err := doExternalAuth(username, password, nil, "", ip, protocol, nil)
  970. if err != nil {
  971. return user, err
  972. }
  973. return checkUserAndPass(&user, password, ip, protocol)
  974. }
  975. if config.PreLoginHook != "" {
  976. user, err := executePreLoginHook(username, LoginMethodPassword, ip, protocol)
  977. if err != nil {
  978. return user, err
  979. }
  980. return checkUserAndPass(&user, password, ip, protocol)
  981. }
  982. return provider.validateUserAndPass(username, password, ip, protocol)
  983. }
  984. // CheckUserAndPubKey retrieves the SFTP user with the given username and public key if a match is found or an error
  985. func CheckUserAndPubKey(username string, pubKey []byte, ip, protocol string, isSSHCert bool) (User, string, error) {
  986. username = config.convertName(username)
  987. if plugin.Handler.HasAuthScope(plugin.AuthScopePublicKey) {
  988. user, err := doPluginAuth(username, "", pubKey, ip, protocol, nil, plugin.AuthScopePublicKey)
  989. if err != nil {
  990. return user, "", err
  991. }
  992. return checkUserAndPubKey(&user, pubKey, isSSHCert)
  993. }
  994. if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&2 != 0) {
  995. user, err := doExternalAuth(username, "", pubKey, "", ip, protocol, nil)
  996. if err != nil {
  997. return user, "", err
  998. }
  999. return checkUserAndPubKey(&user, pubKey, isSSHCert)
  1000. }
  1001. if config.PreLoginHook != "" {
  1002. user, err := executePreLoginHook(username, SSHLoginMethodPublicKey, ip, protocol)
  1003. if err != nil {
  1004. return user, "", err
  1005. }
  1006. return checkUserAndPubKey(&user, pubKey, isSSHCert)
  1007. }
  1008. return provider.validateUserAndPubKey(username, pubKey, isSSHCert)
  1009. }
  1010. // CheckKeyboardInteractiveAuth checks the keyboard interactive authentication and returns
  1011. // the authenticated user or an error
  1012. func CheckKeyboardInteractiveAuth(username, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (User, error) {
  1013. var user User
  1014. var err error
  1015. username = config.convertName(username)
  1016. if plugin.Handler.HasAuthScope(plugin.AuthScopeKeyboardInteractive) {
  1017. user, err = doPluginAuth(username, "", nil, ip, protocol, nil, plugin.AuthScopeKeyboardInteractive)
  1018. } else if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&4 != 0) {
  1019. user, err = doExternalAuth(username, "", nil, "1", ip, protocol, nil)
  1020. } else if config.PreLoginHook != "" {
  1021. user, err = executePreLoginHook(username, SSHLoginMethodKeyboardInteractive, ip, protocol)
  1022. } else {
  1023. user, err = provider.userExists(username)
  1024. }
  1025. if err != nil {
  1026. return user, err
  1027. }
  1028. return doKeyboardInteractiveAuth(&user, authHook, client, ip, protocol)
  1029. }
  1030. // GetUserAfterIDPAuth returns the SFTPGo user with the specified username
  1031. // after a successful authentication with an external identity provider.
  1032. // If a pre-login hook is defined it will be executed so the SFTPGo user
  1033. // can be created if it does not exist
  1034. func GetUserAfterIDPAuth(username, ip, protocol string) (User, error) {
  1035. if config.PreLoginHook != "" {
  1036. return executePreLoginHook(username, LoginMethodIDP, ip, protocol)
  1037. }
  1038. return UserExists(username)
  1039. }
  1040. // GetDefenderHosts returns hosts that are banned or for which some violations have been detected
  1041. func GetDefenderHosts(from int64, limit int) ([]DefenderEntry, error) {
  1042. return provider.getDefenderHosts(from, limit)
  1043. }
  1044. // GetDefenderHostByIP returns a defender host by ip, if any
  1045. func GetDefenderHostByIP(ip string, from int64) (DefenderEntry, error) {
  1046. return provider.getDefenderHostByIP(ip, from)
  1047. }
  1048. // IsDefenderHostBanned returns a defender entry and no error if the specified host is banned
  1049. func IsDefenderHostBanned(ip string) (DefenderEntry, error) {
  1050. return provider.isDefenderHostBanned(ip)
  1051. }
  1052. // UpdateDefenderBanTime increments ban time for the specified ip
  1053. func UpdateDefenderBanTime(ip string, minutes int) error {
  1054. return provider.updateDefenderBanTime(ip, minutes)
  1055. }
  1056. // DeleteDefenderHost removes the specified IP from the defender lists
  1057. func DeleteDefenderHost(ip string) error {
  1058. return provider.deleteDefenderHost(ip)
  1059. }
  1060. // AddDefenderEvent adds an event for the given IP with the given score
  1061. // and returns the host with the updated score
  1062. func AddDefenderEvent(ip string, score int, from int64) (DefenderEntry, error) {
  1063. if err := provider.addDefenderEvent(ip, score); err != nil {
  1064. return DefenderEntry{}, err
  1065. }
  1066. return provider.getDefenderHostByIP(ip, from)
  1067. }
  1068. // SetDefenderBanTime sets the ban time for the specified IP
  1069. func SetDefenderBanTime(ip string, banTime int64) error {
  1070. return provider.setDefenderBanTime(ip, banTime)
  1071. }
  1072. // CleanupDefender removes events and hosts older than "from" from the data provider
  1073. func CleanupDefender(from int64) error {
  1074. return provider.cleanupDefender(from)
  1075. }
  1076. // UpdateShareLastUse updates the LastUseAt and UsedTokens for the given share
  1077. func UpdateShareLastUse(share *Share, numTokens int) error {
  1078. return provider.updateShareLastUse(share.ShareID, numTokens)
  1079. }
  1080. // UpdateAPIKeyLastUse updates the LastUseAt field for the given API key
  1081. func UpdateAPIKeyLastUse(apiKey *APIKey) error {
  1082. lastUse := util.GetTimeFromMsecSinceEpoch(apiKey.LastUseAt)
  1083. diff := -time.Until(lastUse)
  1084. if diff < 0 || diff > lastLoginMinDelay {
  1085. return provider.updateAPIKeyLastUse(apiKey.KeyID)
  1086. }
  1087. return nil
  1088. }
  1089. // UpdateLastLogin updates the last login field for the given SFTPGo user
  1090. func UpdateLastLogin(user *User) {
  1091. delay := lastLoginMinDelay
  1092. if user.Filters.ExternalAuthCacheTime > 0 {
  1093. delay = time.Duration(user.Filters.ExternalAuthCacheTime) * time.Second
  1094. }
  1095. if user.LastLogin <= user.UpdatedAt || !isLastActivityRecent(user.LastLogin, delay) {
  1096. err := provider.updateLastLogin(user.Username)
  1097. if err == nil {
  1098. webDAVUsersCache.updateLastLogin(user.Username)
  1099. }
  1100. }
  1101. }
  1102. // UpdateAdminLastLogin updates the last login field for the given SFTPGo admin
  1103. func UpdateAdminLastLogin(admin *Admin) {
  1104. if !isLastActivityRecent(admin.LastLogin, lastLoginMinDelay) {
  1105. provider.updateAdminLastLogin(admin.Username) //nolint:errcheck
  1106. }
  1107. }
  1108. // UpdateUserQuota updates the quota for the given SFTPGo user adding filesAdd and sizeAdd.
  1109. // If reset is true filesAdd and sizeAdd indicates the total files and the total size instead of the difference.
  1110. func UpdateUserQuota(user *User, filesAdd int, sizeAdd int64, reset bool) error {
  1111. if config.TrackQuota == 0 {
  1112. return util.NewMethodDisabledError(trackQuotaDisabledError)
  1113. } else if config.TrackQuota == 2 && !reset && !user.HasQuotaRestrictions() {
  1114. return nil
  1115. }
  1116. if filesAdd == 0 && sizeAdd == 0 && !reset {
  1117. return nil
  1118. }
  1119. if config.DelayedQuotaUpdate == 0 || reset {
  1120. if reset {
  1121. delayedQuotaUpdater.resetUserQuota(user.Username)
  1122. }
  1123. return provider.updateQuota(user.Username, filesAdd, sizeAdd, reset)
  1124. }
  1125. delayedQuotaUpdater.updateUserQuota(user.Username, filesAdd, sizeAdd)
  1126. return nil
  1127. }
  1128. // UpdateVirtualFolderQuota updates the quota for the given virtual folder adding filesAdd and sizeAdd.
  1129. // If reset is true filesAdd and sizeAdd indicates the total files and the total size instead of the difference.
  1130. func UpdateVirtualFolderQuota(vfolder *vfs.BaseVirtualFolder, filesAdd int, sizeAdd int64, reset bool) error {
  1131. if config.TrackQuota == 0 {
  1132. return util.NewMethodDisabledError(trackQuotaDisabledError)
  1133. }
  1134. if filesAdd == 0 && sizeAdd == 0 && !reset {
  1135. return nil
  1136. }
  1137. if config.DelayedQuotaUpdate == 0 || reset {
  1138. if reset {
  1139. delayedQuotaUpdater.resetFolderQuota(vfolder.Name)
  1140. }
  1141. return provider.updateFolderQuota(vfolder.Name, filesAdd, sizeAdd, reset)
  1142. }
  1143. delayedQuotaUpdater.updateFolderQuota(vfolder.Name, filesAdd, sizeAdd)
  1144. return nil
  1145. }
  1146. // UpdateUserTransferQuota updates the transfer quota for the given SFTPGo user.
  1147. // If reset is true uploadSize and downloadSize indicates the actual sizes instead of the difference.
  1148. func UpdateUserTransferQuota(user *User, uploadSize, downloadSize int64, reset bool) error {
  1149. if config.TrackQuota == 0 {
  1150. return util.NewMethodDisabledError(trackQuotaDisabledError)
  1151. } else if config.TrackQuota == 2 && !reset && !user.HasTransferQuotaRestrictions() {
  1152. return nil
  1153. }
  1154. if downloadSize == 0 && uploadSize == 0 && !reset {
  1155. return nil
  1156. }
  1157. if config.DelayedQuotaUpdate == 0 || reset {
  1158. if reset {
  1159. delayedQuotaUpdater.resetUserTransferQuota(user.Username)
  1160. }
  1161. return provider.updateTransferQuota(user.Username, uploadSize, downloadSize, reset)
  1162. }
  1163. delayedQuotaUpdater.updateUserTransferQuota(user.Username, uploadSize, downloadSize)
  1164. return nil
  1165. }
  1166. // GetUsedQuota returns the used quota for the given SFTPGo user.
  1167. func GetUsedQuota(username string) (int, int64, int64, int64, error) {
  1168. if config.TrackQuota == 0 {
  1169. return 0, 0, 0, 0, util.NewMethodDisabledError(trackQuotaDisabledError)
  1170. }
  1171. files, size, ulTransferSize, dlTransferSize, err := provider.getUsedQuota(username)
  1172. if err != nil {
  1173. return files, size, ulTransferSize, dlTransferSize, err
  1174. }
  1175. delayedFiles, delayedSize := delayedQuotaUpdater.getUserPendingQuota(username)
  1176. delayedUlTransferSize, delayedDLTransferSize := delayedQuotaUpdater.getUserPendingTransferQuota(username)
  1177. return files + delayedFiles, size + delayedSize, ulTransferSize + delayedUlTransferSize,
  1178. dlTransferSize + delayedDLTransferSize, err
  1179. }
  1180. // GetUsedVirtualFolderQuota returns the used quota for the given virtual folder.
  1181. func GetUsedVirtualFolderQuota(name string) (int, int64, error) {
  1182. if config.TrackQuota == 0 {
  1183. return 0, 0, util.NewMethodDisabledError(trackQuotaDisabledError)
  1184. }
  1185. files, size, err := provider.getUsedFolderQuota(name)
  1186. if err != nil {
  1187. return files, size, err
  1188. }
  1189. delayedFiles, delayedSize := delayedQuotaUpdater.getFolderPendingQuota(name)
  1190. return files + delayedFiles, size + delayedSize, err
  1191. }
  1192. // AddShare adds a new share
  1193. func AddShare(share *Share, executor, ipAddress string) error {
  1194. err := provider.addShare(share)
  1195. if err == nil {
  1196. executeAction(operationAdd, executor, ipAddress, actionObjectShare, share.ShareID, share)
  1197. }
  1198. return err
  1199. }
  1200. // UpdateShare updates an existing share
  1201. func UpdateShare(share *Share, executor, ipAddress string) error {
  1202. err := provider.updateShare(share)
  1203. if err == nil {
  1204. executeAction(operationUpdate, executor, ipAddress, actionObjectShare, share.ShareID, share)
  1205. }
  1206. return err
  1207. }
  1208. // DeleteShare deletes an existing share
  1209. func DeleteShare(shareID string, executor, ipAddress string) error {
  1210. share, err := provider.shareExists(shareID, executor)
  1211. if err != nil {
  1212. return err
  1213. }
  1214. err = provider.deleteShare(&share)
  1215. if err == nil {
  1216. executeAction(operationDelete, executor, ipAddress, actionObjectShare, shareID, &share)
  1217. }
  1218. return err
  1219. }
  1220. // ShareExists returns the share with the given ID if it exists
  1221. func ShareExists(shareID, username string) (Share, error) {
  1222. if shareID == "" {
  1223. return Share{}, util.NewRecordNotFoundError(fmt.Sprintf("Share %#v does not exist", shareID))
  1224. }
  1225. return provider.shareExists(shareID, username)
  1226. }
  1227. // AddAPIKey adds a new API key
  1228. func AddAPIKey(apiKey *APIKey, executor, ipAddress string) error {
  1229. err := provider.addAPIKey(apiKey)
  1230. if err == nil {
  1231. executeAction(operationAdd, executor, ipAddress, actionObjectAPIKey, apiKey.KeyID, apiKey)
  1232. }
  1233. return err
  1234. }
  1235. // UpdateAPIKey updates an existing API key
  1236. func UpdateAPIKey(apiKey *APIKey, executor, ipAddress string) error {
  1237. err := provider.updateAPIKey(apiKey)
  1238. if err == nil {
  1239. executeAction(operationUpdate, executor, ipAddress, actionObjectAPIKey, apiKey.KeyID, apiKey)
  1240. }
  1241. return err
  1242. }
  1243. // DeleteAPIKey deletes an existing API key
  1244. func DeleteAPIKey(keyID string, executor, ipAddress string) error {
  1245. apiKey, err := provider.apiKeyExists(keyID)
  1246. if err != nil {
  1247. return err
  1248. }
  1249. err = provider.deleteAPIKey(&apiKey)
  1250. if err == nil {
  1251. executeAction(operationDelete, executor, ipAddress, actionObjectAPIKey, apiKey.KeyID, &apiKey)
  1252. }
  1253. return err
  1254. }
  1255. // APIKeyExists returns the API key with the given ID if it exists
  1256. func APIKeyExists(keyID string) (APIKey, error) {
  1257. if keyID == "" {
  1258. return APIKey{}, util.NewRecordNotFoundError(fmt.Sprintf("API key %#v does not exist", keyID))
  1259. }
  1260. return provider.apiKeyExists(keyID)
  1261. }
  1262. // HasAdmin returns true if the first admin has been created
  1263. // and so SFTPGo is ready to be used
  1264. func HasAdmin() bool {
  1265. return atomic.LoadInt32(&isAdminCreated) > 0
  1266. }
  1267. // AddAdmin adds a new SFTPGo admin
  1268. func AddAdmin(admin *Admin, executor, ipAddress string) error {
  1269. admin.Filters.RecoveryCodes = nil
  1270. admin.Filters.TOTPConfig = AdminTOTPConfig{
  1271. Enabled: false,
  1272. }
  1273. admin.Username = config.convertName(admin.Username)
  1274. err := provider.addAdmin(admin)
  1275. if err == nil {
  1276. atomic.StoreInt32(&isAdminCreated, 1)
  1277. executeAction(operationAdd, executor, ipAddress, actionObjectAdmin, admin.Username, admin)
  1278. }
  1279. return err
  1280. }
  1281. // UpdateAdmin updates an existing SFTPGo admin
  1282. func UpdateAdmin(admin *Admin, executor, ipAddress string) error {
  1283. err := provider.updateAdmin(admin)
  1284. if err == nil {
  1285. executeAction(operationUpdate, executor, ipAddress, actionObjectAdmin, admin.Username, admin)
  1286. }
  1287. return err
  1288. }
  1289. // DeleteAdmin deletes an existing SFTPGo admin
  1290. func DeleteAdmin(username, executor, ipAddress string) error {
  1291. username = config.convertName(username)
  1292. admin, err := provider.adminExists(username)
  1293. if err != nil {
  1294. return err
  1295. }
  1296. err = provider.deleteAdmin(&admin)
  1297. if err == nil {
  1298. executeAction(operationDelete, executor, ipAddress, actionObjectAdmin, admin.Username, &admin)
  1299. }
  1300. return err
  1301. }
  1302. // AdminExists returns the admin with the given username if it exists
  1303. func AdminExists(username string) (Admin, error) {
  1304. username = config.convertName(username)
  1305. return provider.adminExists(username)
  1306. }
  1307. // UserExists checks if the given SFTPGo username exists, returns an error if no match is found
  1308. func UserExists(username string) (User, error) {
  1309. username = config.convertName(username)
  1310. return provider.userExists(username)
  1311. }
  1312. // AddUser adds a new SFTPGo user.
  1313. func AddUser(user *User, executor, ipAddress string) error {
  1314. user.Filters.RecoveryCodes = nil
  1315. user.Filters.TOTPConfig = UserTOTPConfig{
  1316. Enabled: false,
  1317. }
  1318. user.Username = config.convertName(user.Username)
  1319. err := provider.addUser(user)
  1320. if err == nil {
  1321. executeAction(operationAdd, executor, ipAddress, actionObjectUser, user.Username, user)
  1322. }
  1323. return err
  1324. }
  1325. // UpdateUser updates an existing SFTPGo user.
  1326. func UpdateUser(user *User, executor, ipAddress string) error {
  1327. err := provider.updateUser(user)
  1328. if err == nil {
  1329. webDAVUsersCache.swap(user)
  1330. cachedPasswords.Remove(user.Username)
  1331. executeAction(operationUpdate, executor, ipAddress, actionObjectUser, user.Username, user)
  1332. }
  1333. return err
  1334. }
  1335. // DeleteUser deletes an existing SFTPGo user.
  1336. func DeleteUser(username, executor, ipAddress string) error {
  1337. username = config.convertName(username)
  1338. user, err := provider.userExists(username)
  1339. if err != nil {
  1340. return err
  1341. }
  1342. err = provider.deleteUser(&user)
  1343. if err == nil {
  1344. RemoveCachedWebDAVUser(user.Username)
  1345. delayedQuotaUpdater.resetUserQuota(username)
  1346. cachedPasswords.Remove(username)
  1347. executeAction(operationDelete, executor, ipAddress, actionObjectUser, user.Username, &user)
  1348. }
  1349. return err
  1350. }
  1351. // AddActiveTransfer stores the specified transfer
  1352. func AddActiveTransfer(transfer ActiveTransfer) {
  1353. if err := provider.addActiveTransfer(transfer); err != nil {
  1354. providerLog(logger.LevelError, "unable to add transfer id %v, connection id %v: %v",
  1355. transfer.ID, transfer.ConnID, err)
  1356. }
  1357. }
  1358. // UpdateActiveTransferSizes updates the current upload and download sizes for the specified transfer
  1359. func UpdateActiveTransferSizes(ulSize, dlSize, transferID int64, connectionID string) {
  1360. if err := provider.updateActiveTransferSizes(ulSize, dlSize, transferID, connectionID); err != nil {
  1361. providerLog(logger.LevelError, "unable to update sizes for transfer id %v, connection id %v: %v",
  1362. transferID, connectionID, err)
  1363. }
  1364. }
  1365. // RemoveActiveTransfer removes the specified transfer
  1366. func RemoveActiveTransfer(transferID int64, connectionID string) {
  1367. if err := provider.removeActiveTransfer(transferID, connectionID); err != nil {
  1368. providerLog(logger.LevelError, "unable to delete transfer id %v, connection id %v: %v",
  1369. transferID, connectionID, err)
  1370. }
  1371. }
  1372. // CleanupActiveTransfers removes the transfer before the specified time
  1373. func CleanupActiveTransfers(before time.Time) error {
  1374. err := provider.cleanupActiveTransfers(before)
  1375. if err == nil {
  1376. providerLog(logger.LevelDebug, "deleted active transfers updated before: %v", before)
  1377. } else {
  1378. providerLog(logger.LevelError, "error deleting active transfers updated before %v: %v", before, err)
  1379. }
  1380. return err
  1381. }
  1382. // GetActiveTransfers retrieves the active transfers with an update time after the specified value
  1383. func GetActiveTransfers(from time.Time) ([]ActiveTransfer, error) {
  1384. return provider.getActiveTransfers(from)
  1385. }
  1386. // ReloadConfig reloads provider configuration.
  1387. // Currently only implemented for memory provider, allows to reload the users
  1388. // from the configured file, if defined
  1389. func ReloadConfig() error {
  1390. return provider.reloadConfig()
  1391. }
  1392. // GetShares returns an array of shares respecting limit and offset
  1393. func GetShares(limit, offset int, order, username string) ([]Share, error) {
  1394. return provider.getShares(limit, offset, order, username)
  1395. }
  1396. // GetAPIKeys returns an array of API keys respecting limit and offset
  1397. func GetAPIKeys(limit, offset int, order string) ([]APIKey, error) {
  1398. return provider.getAPIKeys(limit, offset, order)
  1399. }
  1400. // GetAdmins returns an array of admins respecting limit and offset
  1401. func GetAdmins(limit, offset int, order string) ([]Admin, error) {
  1402. return provider.getAdmins(limit, offset, order)
  1403. }
  1404. // GetUsers returns an array of users respecting limit and offset and filtered by username exact match if not empty
  1405. func GetUsers(limit, offset int, order string) ([]User, error) {
  1406. return provider.getUsers(limit, offset, order)
  1407. }
  1408. // GetUsersForQuotaCheck returns the users with the fields required for a quota check
  1409. func GetUsersForQuotaCheck(toFetch map[string]bool) ([]User, error) {
  1410. return provider.getUsersForQuotaCheck(toFetch)
  1411. }
  1412. // AddFolder adds a new virtual folder.
  1413. func AddFolder(folder *vfs.BaseVirtualFolder) error {
  1414. folder.Name = config.convertName(folder.Name)
  1415. return provider.addFolder(folder)
  1416. }
  1417. // UpdateFolder updates the specified virtual folder
  1418. func UpdateFolder(folder *vfs.BaseVirtualFolder, users []string, executor, ipAddress string) error {
  1419. err := provider.updateFolder(folder)
  1420. if err == nil {
  1421. for _, user := range users {
  1422. provider.setUpdatedAt(user)
  1423. u, err := provider.userExists(user)
  1424. if err == nil {
  1425. webDAVUsersCache.swap(&u)
  1426. executeAction(operationUpdate, executor, ipAddress, actionObjectUser, u.Username, &u)
  1427. } else {
  1428. RemoveCachedWebDAVUser(user)
  1429. }
  1430. }
  1431. }
  1432. return err
  1433. }
  1434. // DeleteFolder deletes an existing folder.
  1435. func DeleteFolder(folderName, executor, ipAddress string) error {
  1436. folderName = config.convertName(folderName)
  1437. folder, err := provider.getFolderByName(folderName)
  1438. if err != nil {
  1439. return err
  1440. }
  1441. err = provider.deleteFolder(&folder)
  1442. if err == nil {
  1443. for _, user := range folder.Users {
  1444. provider.setUpdatedAt(user)
  1445. u, err := provider.userExists(user)
  1446. if err == nil {
  1447. executeAction(operationUpdate, executor, ipAddress, actionObjectUser, u.Username, &u)
  1448. }
  1449. RemoveCachedWebDAVUser(user)
  1450. }
  1451. delayedQuotaUpdater.resetFolderQuota(folderName)
  1452. }
  1453. return err
  1454. }
  1455. // GetFolderByName returns the folder with the specified name if any
  1456. func GetFolderByName(name string) (vfs.BaseVirtualFolder, error) {
  1457. name = config.convertName(name)
  1458. return provider.getFolderByName(name)
  1459. }
  1460. // GetFolders returns an array of folders respecting limit and offset
  1461. func GetFolders(limit, offset int, order string) ([]vfs.BaseVirtualFolder, error) {
  1462. return provider.getFolders(limit, offset, order)
  1463. }
  1464. // DumpData returns all users, folders, admins, api keys, shares
  1465. func DumpData() (BackupData, error) {
  1466. var data BackupData
  1467. users, err := provider.dumpUsers()
  1468. if err != nil {
  1469. return data, err
  1470. }
  1471. folders, err := provider.dumpFolders()
  1472. if err != nil {
  1473. return data, err
  1474. }
  1475. admins, err := provider.dumpAdmins()
  1476. if err != nil {
  1477. return data, err
  1478. }
  1479. apiKeys, err := provider.dumpAPIKeys()
  1480. if err != nil {
  1481. return data, err
  1482. }
  1483. shares, err := provider.dumpShares()
  1484. if err != nil {
  1485. return data, err
  1486. }
  1487. data.Users = users
  1488. data.Folders = folders
  1489. data.Admins = admins
  1490. data.APIKeys = apiKeys
  1491. data.Shares = shares
  1492. data.Version = DumpVersion
  1493. return data, err
  1494. }
  1495. // ParseDumpData tries to parse data as BackupData
  1496. func ParseDumpData(data []byte) (BackupData, error) {
  1497. var dump BackupData
  1498. err := json.Unmarshal(data, &dump)
  1499. return dump, err
  1500. }
  1501. // GetProviderConfig returns the current provider configuration
  1502. func GetProviderConfig() Config {
  1503. return config
  1504. }
  1505. // GetProviderStatus returns an error if the provider is not available
  1506. func GetProviderStatus() ProviderStatus {
  1507. err := provider.checkAvailability()
  1508. status := ProviderStatus{
  1509. Driver: config.Driver,
  1510. }
  1511. if err == nil {
  1512. status.IsActive = true
  1513. } else {
  1514. status.IsActive = false
  1515. status.Error = err.Error()
  1516. }
  1517. return status
  1518. }
  1519. // Close releases all provider resources.
  1520. // This method is used in test cases.
  1521. // Closing an uninitialized provider is not supported
  1522. func Close() error {
  1523. stopScheduler()
  1524. return provider.close()
  1525. }
  1526. func createProvider(basePath string) error {
  1527. var err error
  1528. sqlPlaceholders = getSQLPlaceholders()
  1529. if err = validateSQLTablesPrefix(); err != nil {
  1530. return err
  1531. }
  1532. logSender = fmt.Sprintf("dataprovider_%v", config.Driver)
  1533. switch config.Driver {
  1534. case SQLiteDataProviderName:
  1535. return initializeSQLiteProvider(basePath)
  1536. case PGSQLDataProviderName, CockroachDataProviderName:
  1537. return initializePGSQLProvider()
  1538. case MySQLDataProviderName:
  1539. return initializeMySQLProvider()
  1540. case BoltDataProviderName:
  1541. return initializeBoltProvider(basePath)
  1542. case MemoryDataProviderName:
  1543. initializeMemoryProvider(basePath)
  1544. return nil
  1545. default:
  1546. return fmt.Errorf("unsupported data provider: %v", config.Driver)
  1547. }
  1548. }
  1549. func buildUserHomeDir(user *User) {
  1550. if user.HomeDir == "" {
  1551. if config.UsersBaseDir != "" {
  1552. user.HomeDir = filepath.Join(config.UsersBaseDir, user.Username)
  1553. return
  1554. }
  1555. switch user.FsConfig.Provider {
  1556. case sdk.SFTPFilesystemProvider, sdk.S3FilesystemProvider, sdk.AzureBlobFilesystemProvider, sdk.GCSFilesystemProvider:
  1557. if tempPath != "" {
  1558. user.HomeDir = filepath.Join(tempPath, user.Username)
  1559. } else {
  1560. user.HomeDir = filepath.Join(os.TempDir(), user.Username)
  1561. }
  1562. }
  1563. }
  1564. }
  1565. func isVirtualDirOverlapped(dir1, dir2 string, fullCheck bool) bool {
  1566. if dir1 == dir2 {
  1567. return true
  1568. }
  1569. if fullCheck {
  1570. if len(dir1) > len(dir2) {
  1571. if strings.HasPrefix(dir1, dir2+"/") {
  1572. return true
  1573. }
  1574. }
  1575. if len(dir2) > len(dir1) {
  1576. if strings.HasPrefix(dir2, dir1+"/") {
  1577. return true
  1578. }
  1579. }
  1580. }
  1581. return false
  1582. }
  1583. func validateFolderQuotaLimits(folder vfs.VirtualFolder) error {
  1584. if folder.QuotaSize < -1 {
  1585. return util.NewValidationError(fmt.Sprintf("invalid quota_size: %v folder path %#v", folder.QuotaSize, folder.MappedPath))
  1586. }
  1587. if folder.QuotaFiles < -1 {
  1588. return util.NewValidationError(fmt.Sprintf("invalid quota_file: %v folder path %#v", folder.QuotaFiles, folder.MappedPath))
  1589. }
  1590. if (folder.QuotaSize == -1 && folder.QuotaFiles != -1) || (folder.QuotaFiles == -1 && folder.QuotaSize != -1) {
  1591. return util.NewValidationError(fmt.Sprintf("virtual folder quota_size and quota_files must be both -1 or >= 0, quota_size: %v quota_files: %v",
  1592. folder.QuotaFiles, folder.QuotaSize))
  1593. }
  1594. return nil
  1595. }
  1596. func getVirtualFolderIfInvalid(folder *vfs.BaseVirtualFolder) *vfs.BaseVirtualFolder {
  1597. if err := ValidateFolder(folder); err == nil {
  1598. return folder
  1599. }
  1600. // we try to get the folder from the data provider if only the Name is populated
  1601. if folder.MappedPath != "" {
  1602. return folder
  1603. }
  1604. if folder.Name == "" {
  1605. return folder
  1606. }
  1607. if folder.FsConfig.Provider != sdk.LocalFilesystemProvider {
  1608. return folder
  1609. }
  1610. if f, err := GetFolderByName(folder.Name); err == nil {
  1611. return &f
  1612. }
  1613. return folder
  1614. }
  1615. func validateUserVirtualFolders(user *User) error {
  1616. if len(user.VirtualFolders) == 0 {
  1617. user.VirtualFolders = []vfs.VirtualFolder{}
  1618. return nil
  1619. }
  1620. var virtualFolders []vfs.VirtualFolder
  1621. folderNames := make(map[string]bool)
  1622. for _, v := range user.VirtualFolders {
  1623. cleanedVPath := util.CleanPath(v.VirtualPath)
  1624. if err := validateFolderQuotaLimits(v); err != nil {
  1625. return err
  1626. }
  1627. folder := getVirtualFolderIfInvalid(&v.BaseVirtualFolder)
  1628. if err := ValidateFolder(folder); err != nil {
  1629. return err
  1630. }
  1631. if folderNames[folder.Name] {
  1632. return util.NewValidationError(fmt.Sprintf("the folder %#v is duplicated", folder.Name))
  1633. }
  1634. for _, vFolder := range virtualFolders {
  1635. if isVirtualDirOverlapped(vFolder.VirtualPath, cleanedVPath, false) {
  1636. return util.NewValidationError(fmt.Sprintf("invalid virtual folder %#v, it overlaps with virtual folder %#v",
  1637. v.VirtualPath, vFolder.VirtualPath))
  1638. }
  1639. }
  1640. virtualFolders = append(virtualFolders, vfs.VirtualFolder{
  1641. BaseVirtualFolder: *folder,
  1642. VirtualPath: cleanedVPath,
  1643. QuotaSize: v.QuotaSize,
  1644. QuotaFiles: v.QuotaFiles,
  1645. })
  1646. folderNames[folder.Name] = true
  1647. }
  1648. user.VirtualFolders = virtualFolders
  1649. return nil
  1650. }
  1651. func validateUserTOTPConfig(c *UserTOTPConfig, username string) error {
  1652. if !c.Enabled {
  1653. c.ConfigName = ""
  1654. c.Secret = kms.NewEmptySecret()
  1655. c.Protocols = nil
  1656. return nil
  1657. }
  1658. if c.ConfigName == "" {
  1659. return util.NewValidationError("totp: config name is mandatory")
  1660. }
  1661. if !util.IsStringInSlice(c.ConfigName, mfa.GetAvailableTOTPConfigNames()) {
  1662. return util.NewValidationError(fmt.Sprintf("totp: config name %#v not found", c.ConfigName))
  1663. }
  1664. if c.Secret.IsEmpty() {
  1665. return util.NewValidationError("totp: secret is mandatory")
  1666. }
  1667. if c.Secret.IsPlain() {
  1668. c.Secret.SetAdditionalData(username)
  1669. if err := c.Secret.Encrypt(); err != nil {
  1670. return util.NewValidationError(fmt.Sprintf("totp: unable to encrypt secret: %v", err))
  1671. }
  1672. }
  1673. if len(c.Protocols) == 0 {
  1674. return util.NewValidationError("totp: specify at least one protocol")
  1675. }
  1676. for _, protocol := range c.Protocols {
  1677. if !util.IsStringInSlice(protocol, MFAProtocols) {
  1678. return util.NewValidationError(fmt.Sprintf("totp: invalid protocol %#v", protocol))
  1679. }
  1680. }
  1681. return nil
  1682. }
  1683. func validateUserRecoveryCodes(user *User) error {
  1684. for i := 0; i < len(user.Filters.RecoveryCodes); i++ {
  1685. code := &user.Filters.RecoveryCodes[i]
  1686. if code.Secret.IsEmpty() {
  1687. return util.NewValidationError("mfa: recovery code cannot be empty")
  1688. }
  1689. if code.Secret.IsPlain() {
  1690. code.Secret.SetAdditionalData(user.Username)
  1691. if err := code.Secret.Encrypt(); err != nil {
  1692. return util.NewValidationError(fmt.Sprintf("mfa: unable to encrypt recovery code: %v", err))
  1693. }
  1694. }
  1695. }
  1696. return nil
  1697. }
  1698. func validatePermissions(user *User) error {
  1699. if len(user.Permissions) == 0 {
  1700. return util.NewValidationError("please grant some permissions to this user")
  1701. }
  1702. permissions := make(map[string][]string)
  1703. if _, ok := user.Permissions["/"]; !ok {
  1704. return util.NewValidationError("permissions for the root dir \"/\" must be set")
  1705. }
  1706. for dir, perms := range user.Permissions {
  1707. if len(perms) == 0 && dir == "/" {
  1708. return util.NewValidationError(fmt.Sprintf("no permissions granted for the directory: %#v", dir))
  1709. }
  1710. if len(perms) > len(ValidPerms) {
  1711. return util.NewValidationError("invalid permissions")
  1712. }
  1713. for _, p := range perms {
  1714. if !util.IsStringInSlice(p, ValidPerms) {
  1715. return util.NewValidationError(fmt.Sprintf("invalid permission: %#v", p))
  1716. }
  1717. }
  1718. cleanedDir := filepath.ToSlash(path.Clean(dir))
  1719. if cleanedDir != "/" {
  1720. cleanedDir = strings.TrimSuffix(cleanedDir, "/")
  1721. }
  1722. if !path.IsAbs(cleanedDir) {
  1723. return util.NewValidationError(fmt.Sprintf("cannot set permissions for non absolute path: %#v", dir))
  1724. }
  1725. if dir != cleanedDir && cleanedDir == "/" {
  1726. return util.NewValidationError(fmt.Sprintf("cannot set permissions for invalid subdirectory: %#v is an alias for \"/\"", dir))
  1727. }
  1728. if util.IsStringInSlice(PermAny, perms) {
  1729. permissions[cleanedDir] = []string{PermAny}
  1730. } else {
  1731. permissions[cleanedDir] = util.RemoveDuplicates(perms)
  1732. }
  1733. }
  1734. user.Permissions = permissions
  1735. return nil
  1736. }
  1737. func validatePublicKeys(user *User) error {
  1738. if len(user.PublicKeys) == 0 {
  1739. user.PublicKeys = []string{}
  1740. }
  1741. var validatedKeys []string
  1742. for i, k := range user.PublicKeys {
  1743. if k == "" {
  1744. continue
  1745. }
  1746. _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
  1747. if err != nil {
  1748. return util.NewValidationError(fmt.Sprintf("could not parse key nr. %d: %s", i+1, err))
  1749. }
  1750. validatedKeys = append(validatedKeys, k)
  1751. }
  1752. user.PublicKeys = util.RemoveDuplicates(validatedKeys)
  1753. return nil
  1754. }
  1755. func validateFiltersPatternExtensions(user *User) error {
  1756. if len(user.Filters.FilePatterns) == 0 {
  1757. user.Filters.FilePatterns = []sdk.PatternsFilter{}
  1758. return nil
  1759. }
  1760. filteredPaths := []string{}
  1761. var filters []sdk.PatternsFilter
  1762. for _, f := range user.Filters.FilePatterns {
  1763. cleanedPath := filepath.ToSlash(path.Clean(f.Path))
  1764. if !path.IsAbs(cleanedPath) {
  1765. return util.NewValidationError(fmt.Sprintf("invalid path %#v for file patterns filter", f.Path))
  1766. }
  1767. if util.IsStringInSlice(cleanedPath, filteredPaths) {
  1768. return util.NewValidationError(fmt.Sprintf("duplicate file patterns filter for path %#v", f.Path))
  1769. }
  1770. if len(f.AllowedPatterns) == 0 && len(f.DeniedPatterns) == 0 {
  1771. return util.NewValidationError(fmt.Sprintf("empty file patterns filter for path %#v", f.Path))
  1772. }
  1773. if f.DenyPolicy < sdk.DenyPolicyDefault || f.DenyPolicy > sdk.DenyPolicyHide {
  1774. return util.NewValidationError(fmt.Sprintf("invalid deny policy %v for path %#v", f.DenyPolicy, f.Path))
  1775. }
  1776. f.Path = cleanedPath
  1777. allowed := make([]string, 0, len(f.AllowedPatterns))
  1778. denied := make([]string, 0, len(f.DeniedPatterns))
  1779. for _, pattern := range f.AllowedPatterns {
  1780. _, err := path.Match(pattern, "abc")
  1781. if err != nil {
  1782. return util.NewValidationError(fmt.Sprintf("invalid file pattern filter %#v", pattern))
  1783. }
  1784. allowed = append(allowed, strings.ToLower(pattern))
  1785. }
  1786. for _, pattern := range f.DeniedPatterns {
  1787. _, err := path.Match(pattern, "abc")
  1788. if err != nil {
  1789. return util.NewValidationError(fmt.Sprintf("invalid file pattern filter %#v", pattern))
  1790. }
  1791. denied = append(denied, strings.ToLower(pattern))
  1792. }
  1793. f.AllowedPatterns = util.RemoveDuplicates(allowed)
  1794. f.DeniedPatterns = util.RemoveDuplicates(denied)
  1795. filters = append(filters, f)
  1796. filteredPaths = append(filteredPaths, cleanedPath)
  1797. }
  1798. user.Filters.FilePatterns = filters
  1799. return nil
  1800. }
  1801. func checkEmptyFiltersStruct(user *User) {
  1802. if len(user.Filters.AllowedIP) == 0 {
  1803. user.Filters.AllowedIP = []string{}
  1804. }
  1805. if len(user.Filters.DeniedIP) == 0 {
  1806. user.Filters.DeniedIP = []string{}
  1807. }
  1808. if len(user.Filters.DeniedLoginMethods) == 0 {
  1809. user.Filters.DeniedLoginMethods = []string{}
  1810. }
  1811. if len(user.Filters.DeniedProtocols) == 0 {
  1812. user.Filters.DeniedProtocols = []string{}
  1813. }
  1814. }
  1815. func validateIPFilters(user *User) error {
  1816. user.Filters.DeniedIP = util.RemoveDuplicates(user.Filters.DeniedIP)
  1817. for _, IPMask := range user.Filters.DeniedIP {
  1818. _, _, err := net.ParseCIDR(IPMask)
  1819. if err != nil {
  1820. return util.NewValidationError(fmt.Sprintf("could not parse denied IP/Mask %#v: %v", IPMask, err))
  1821. }
  1822. }
  1823. user.Filters.AllowedIP = util.RemoveDuplicates(user.Filters.AllowedIP)
  1824. for _, IPMask := range user.Filters.AllowedIP {
  1825. _, _, err := net.ParseCIDR(IPMask)
  1826. if err != nil {
  1827. return util.NewValidationError(fmt.Sprintf("could not parse allowed IP/Mask %#v: %v", IPMask, err))
  1828. }
  1829. }
  1830. return nil
  1831. }
  1832. func validateBandwidthLimit(bl sdk.BandwidthLimit) error {
  1833. if len(bl.Sources) == 0 {
  1834. return util.NewValidationError("no bandwidth limit source specified")
  1835. }
  1836. for _, source := range bl.Sources {
  1837. _, _, err := net.ParseCIDR(source)
  1838. if err != nil {
  1839. return util.NewValidationError(fmt.Sprintf("could not parse bandwidth limit source %#v: %v", source, err))
  1840. }
  1841. }
  1842. return nil
  1843. }
  1844. func validateBandwidthLimitsFilter(user *User) error {
  1845. for idx, bandwidthLimit := range user.Filters.BandwidthLimits {
  1846. if err := validateBandwidthLimit(bandwidthLimit); err != nil {
  1847. return err
  1848. }
  1849. if bandwidthLimit.DownloadBandwidth < 0 {
  1850. user.Filters.BandwidthLimits[idx].DownloadBandwidth = 0
  1851. }
  1852. if bandwidthLimit.UploadBandwidth < 0 {
  1853. user.Filters.BandwidthLimits[idx].UploadBandwidth = 0
  1854. }
  1855. }
  1856. return nil
  1857. }
  1858. func validateTransferLimitsFilter(user *User) error {
  1859. for idx, limit := range user.Filters.DataTransferLimits {
  1860. user.Filters.DataTransferLimits[idx].Sources = util.RemoveDuplicates(limit.Sources)
  1861. if len(limit.Sources) == 0 {
  1862. return util.NewValidationError("no data transfer limit source specified")
  1863. }
  1864. for _, source := range limit.Sources {
  1865. _, _, err := net.ParseCIDR(source)
  1866. if err != nil {
  1867. return util.NewValidationError(fmt.Sprintf("could not parse data transfer limit source %#v: %v", source, err))
  1868. }
  1869. }
  1870. if limit.TotalDataTransfer > 0 {
  1871. user.Filters.DataTransferLimits[idx].UploadDataTransfer = 0
  1872. user.Filters.DataTransferLimits[idx].DownloadDataTransfer = 0
  1873. }
  1874. }
  1875. return nil
  1876. }
  1877. func updateFiltersValues(user *User) {
  1878. if !user.HasExternalAuth() {
  1879. user.Filters.ExternalAuthCacheTime = 0
  1880. }
  1881. if user.Filters.StartDirectory != "" {
  1882. user.Filters.StartDirectory = util.CleanPath(user.Filters.StartDirectory)
  1883. if user.Filters.StartDirectory == "/" {
  1884. user.Filters.StartDirectory = ""
  1885. }
  1886. }
  1887. }
  1888. func validateFilterProtocols(user *User) error {
  1889. if len(user.Filters.DeniedProtocols) >= len(ValidProtocols) {
  1890. return util.NewValidationError("invalid denied_protocols")
  1891. }
  1892. for _, p := range user.Filters.DeniedProtocols {
  1893. if !util.IsStringInSlice(p, ValidProtocols) {
  1894. return util.NewValidationError(fmt.Sprintf("invalid denied protocol %#v", p))
  1895. }
  1896. }
  1897. for _, p := range user.Filters.TwoFactorAuthProtocols {
  1898. if !util.IsStringInSlice(p, MFAProtocols) {
  1899. return util.NewValidationError(fmt.Sprintf("invalid two factor protocol %#v", p))
  1900. }
  1901. }
  1902. return nil
  1903. }
  1904. func validateFilters(user *User) error {
  1905. checkEmptyFiltersStruct(user)
  1906. if err := validateIPFilters(user); err != nil {
  1907. return err
  1908. }
  1909. if err := validateBandwidthLimitsFilter(user); err != nil {
  1910. return err
  1911. }
  1912. if err := validateTransferLimitsFilter(user); err != nil {
  1913. return err
  1914. }
  1915. if len(user.Filters.DeniedLoginMethods) >= len(ValidLoginMethods) {
  1916. return util.NewValidationError("invalid denied_login_methods")
  1917. }
  1918. for _, loginMethod := range user.Filters.DeniedLoginMethods {
  1919. if !util.IsStringInSlice(loginMethod, ValidLoginMethods) {
  1920. return util.NewValidationError(fmt.Sprintf("invalid login method: %#v", loginMethod))
  1921. }
  1922. }
  1923. if err := validateFilterProtocols(user); err != nil {
  1924. return err
  1925. }
  1926. if user.Filters.TLSUsername != "" {
  1927. if !util.IsStringInSlice(string(user.Filters.TLSUsername), validTLSUsernames) {
  1928. return util.NewValidationError(fmt.Sprintf("invalid TLS username: %#v", user.Filters.TLSUsername))
  1929. }
  1930. }
  1931. for _, opts := range user.Filters.WebClient {
  1932. if !util.IsStringInSlice(opts, sdk.WebClientOptions) {
  1933. return util.NewValidationError(fmt.Sprintf("invalid web client options %#v", opts))
  1934. }
  1935. }
  1936. updateFiltersValues(user)
  1937. return validateFiltersPatternExtensions(user)
  1938. }
  1939. func saveGCSCredentials(fsConfig *vfs.Filesystem, helper vfs.ValidatorHelper) error {
  1940. if fsConfig.Provider != sdk.GCSFilesystemProvider {
  1941. return nil
  1942. }
  1943. if fsConfig.GCSConfig.Credentials.GetPayload() == "" {
  1944. return nil
  1945. }
  1946. if config.PreferDatabaseCredentials {
  1947. if fsConfig.GCSConfig.Credentials.IsPlain() {
  1948. fsConfig.GCSConfig.Credentials.SetAdditionalData(helper.GetEncryptionAdditionalData())
  1949. err := fsConfig.GCSConfig.Credentials.Encrypt()
  1950. if err != nil {
  1951. return err
  1952. }
  1953. }
  1954. return nil
  1955. }
  1956. if fsConfig.GCSConfig.Credentials.IsPlain() {
  1957. fsConfig.GCSConfig.Credentials.SetAdditionalData(helper.GetEncryptionAdditionalData())
  1958. err := fsConfig.GCSConfig.Credentials.Encrypt()
  1959. if err != nil {
  1960. return util.NewValidationError(fmt.Sprintf("could not encrypt GCS credentials: %v", err))
  1961. }
  1962. }
  1963. creds, err := json.Marshal(fsConfig.GCSConfig.Credentials)
  1964. if err != nil {
  1965. return util.NewValidationError(fmt.Sprintf("could not marshal GCS credentials: %v", err))
  1966. }
  1967. credentialsFilePath := helper.GetGCSCredentialsFilePath()
  1968. err = os.MkdirAll(filepath.Dir(credentialsFilePath), 0700)
  1969. if err != nil {
  1970. return util.NewValidationError(fmt.Sprintf("could not create GCS credentials dir: %v", err))
  1971. }
  1972. err = os.WriteFile(credentialsFilePath, creds, 0600)
  1973. if err != nil {
  1974. return util.NewValidationError(fmt.Sprintf("could not save GCS credentials: %v", err))
  1975. }
  1976. fsConfig.GCSConfig.Credentials = kms.NewEmptySecret()
  1977. return nil
  1978. }
  1979. func validateBaseParams(user *User) error {
  1980. if user.Username == "" {
  1981. return util.NewValidationError("username is mandatory")
  1982. }
  1983. if user.Email != "" && !emailRegex.MatchString(user.Email) {
  1984. return util.NewValidationError(fmt.Sprintf("email %#v is not valid", user.Email))
  1985. }
  1986. if config.NamingRules&1 == 0 && !usernameRegex.MatchString(user.Username) {
  1987. return util.NewValidationError(fmt.Sprintf("username %#v is not valid, the following characters are allowed: a-zA-Z0-9-_.~",
  1988. user.Username))
  1989. }
  1990. if user.HomeDir == "" {
  1991. return util.NewValidationError("home_dir is mandatory")
  1992. }
  1993. // we can have users with no passwords and public keys, they can authenticate via SSH user certs or OIDC
  1994. /*if user.Password == "" && len(user.PublicKeys) == 0 {
  1995. return util.NewValidationError("please set a password or at least a public_key")
  1996. }*/
  1997. if !filepath.IsAbs(user.HomeDir) {
  1998. return util.NewValidationError(fmt.Sprintf("home_dir must be an absolute path, actual value: %v", user.HomeDir))
  1999. }
  2000. if user.DownloadBandwidth < 0 {
  2001. user.DownloadBandwidth = 0
  2002. }
  2003. if user.UploadBandwidth < 0 {
  2004. user.UploadBandwidth = 0
  2005. }
  2006. if user.TotalDataTransfer > 0 {
  2007. // if a total data transfer is defined we reset the separate upload and download limits
  2008. user.UploadDataTransfer = 0
  2009. user.DownloadDataTransfer = 0
  2010. }
  2011. return nil
  2012. }
  2013. func createUserPasswordHash(user *User) error {
  2014. if user.Password != "" && !user.IsPasswordHashed() {
  2015. if config.PasswordValidation.Users.MinEntropy > 0 {
  2016. if err := passwordvalidator.Validate(user.Password, config.PasswordValidation.Users.MinEntropy); err != nil {
  2017. return util.NewValidationError(err.Error())
  2018. }
  2019. }
  2020. if config.PasswordHashing.Algo == HashingAlgoBcrypt {
  2021. pwd, err := bcrypt.GenerateFromPassword([]byte(user.Password), config.PasswordHashing.BcryptOptions.Cost)
  2022. if err != nil {
  2023. return err
  2024. }
  2025. user.Password = string(pwd)
  2026. } else {
  2027. pwd, err := argon2id.CreateHash(user.Password, argon2Params)
  2028. if err != nil {
  2029. return err
  2030. }
  2031. user.Password = pwd
  2032. }
  2033. }
  2034. return nil
  2035. }
  2036. // ValidateFolder returns an error if the folder is not valid
  2037. // FIXME: this should be defined as Folder struct method
  2038. func ValidateFolder(folder *vfs.BaseVirtualFolder) error {
  2039. folder.FsConfig.SetEmptySecretsIfNil()
  2040. if folder.Name == "" {
  2041. return util.NewValidationError("folder name is mandatory")
  2042. }
  2043. if config.NamingRules&1 == 0 && !usernameRegex.MatchString(folder.Name) {
  2044. return util.NewValidationError(fmt.Sprintf("folder name %#v is not valid, the following characters are allowed: a-zA-Z0-9-_.~",
  2045. folder.Name))
  2046. }
  2047. if folder.FsConfig.Provider == sdk.LocalFilesystemProvider || folder.FsConfig.Provider == sdk.CryptedFilesystemProvider ||
  2048. folder.MappedPath != "" {
  2049. cleanedMPath := filepath.Clean(folder.MappedPath)
  2050. if !filepath.IsAbs(cleanedMPath) {
  2051. return util.NewValidationError(fmt.Sprintf("invalid folder mapped path %#v", folder.MappedPath))
  2052. }
  2053. folder.MappedPath = cleanedMPath
  2054. }
  2055. if folder.HasRedactedSecret() {
  2056. return errors.New("cannot save a folder with a redacted secret")
  2057. }
  2058. if err := folder.FsConfig.Validate(folder); err != nil {
  2059. return err
  2060. }
  2061. return saveGCSCredentials(&folder.FsConfig, folder)
  2062. }
  2063. // ValidateUser returns an error if the user is not valid
  2064. // FIXME: this should be defined as User struct method
  2065. func ValidateUser(user *User) error {
  2066. user.SetEmptySecretsIfNil()
  2067. buildUserHomeDir(user)
  2068. if err := validateBaseParams(user); err != nil {
  2069. return err
  2070. }
  2071. if err := validatePermissions(user); err != nil {
  2072. return err
  2073. }
  2074. if user.hasRedactedSecret() {
  2075. return util.NewValidationError("cannot save a user with a redacted secret")
  2076. }
  2077. if err := validateUserTOTPConfig(&user.Filters.TOTPConfig, user.Username); err != nil {
  2078. return err
  2079. }
  2080. if err := validateUserRecoveryCodes(user); err != nil {
  2081. return err
  2082. }
  2083. if err := user.FsConfig.Validate(user); err != nil {
  2084. return err
  2085. }
  2086. if err := validateUserVirtualFolders(user); err != nil {
  2087. return err
  2088. }
  2089. if user.Status < 0 || user.Status > 1 {
  2090. return util.NewValidationError(fmt.Sprintf("invalid user status: %v", user.Status))
  2091. }
  2092. if err := createUserPasswordHash(user); err != nil {
  2093. return err
  2094. }
  2095. if err := validatePublicKeys(user); err != nil {
  2096. return err
  2097. }
  2098. if err := validateFilters(user); err != nil {
  2099. return err
  2100. }
  2101. if user.Filters.TOTPConfig.Enabled && util.IsStringInSlice(sdk.WebClientMFADisabled, user.Filters.WebClient) {
  2102. return util.NewValidationError("two-factor authentication cannot be disabled for a user with an active configuration")
  2103. }
  2104. return saveGCSCredentials(&user.FsConfig, user)
  2105. }
  2106. func isPasswordOK(user *User, password string) (bool, error) {
  2107. if config.PasswordCaching {
  2108. found, match := cachedPasswords.Check(user.Username, password)
  2109. if found {
  2110. return match, nil
  2111. }
  2112. }
  2113. match := false
  2114. var err error
  2115. if strings.HasPrefix(user.Password, bcryptPwdPrefix) {
  2116. if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
  2117. return match, ErrInvalidCredentials
  2118. }
  2119. match = true
  2120. } else if strings.HasPrefix(user.Password, argonPwdPrefix) {
  2121. match, err = argon2id.ComparePasswordAndHash(password, user.Password)
  2122. if err != nil {
  2123. providerLog(logger.LevelError, "error comparing password with argon hash: %v", err)
  2124. return match, err
  2125. }
  2126. } else if util.IsStringPrefixInSlice(user.Password, pbkdfPwdPrefixes) {
  2127. match, err = comparePbkdf2PasswordAndHash(password, user.Password)
  2128. if err != nil {
  2129. return match, err
  2130. }
  2131. } else if util.IsStringPrefixInSlice(user.Password, unixPwdPrefixes) {
  2132. match, err = compareUnixPasswordAndHash(user, password)
  2133. if err != nil {
  2134. return match, err
  2135. }
  2136. }
  2137. if err == nil && match {
  2138. cachedPasswords.Add(user.Username, password)
  2139. }
  2140. return match, err
  2141. }
  2142. func checkUserAndTLSCertificate(user *User, protocol string, tlsCert *x509.Certificate) (User, error) {
  2143. err := user.CheckLoginConditions()
  2144. if err != nil {
  2145. return *user, err
  2146. }
  2147. switch protocol {
  2148. case protocolFTP, protocolWebDAV:
  2149. if user.Filters.TLSUsername == sdk.TLSUsernameCN {
  2150. if user.Username == tlsCert.Subject.CommonName {
  2151. return *user, nil
  2152. }
  2153. return *user, fmt.Errorf("CN %#v does not match username %#v", tlsCert.Subject.CommonName, user.Username)
  2154. }
  2155. return *user, errors.New("TLS certificate is not valid")
  2156. default:
  2157. return *user, fmt.Errorf("certificate authentication is not supported for protocol %v", protocol)
  2158. }
  2159. }
  2160. func checkUserAndPass(user *User, password, ip, protocol string) (User, error) {
  2161. err := user.CheckLoginConditions()
  2162. if err != nil {
  2163. return *user, err
  2164. }
  2165. password, err = checkUserPasscode(user, password, protocol)
  2166. if err != nil {
  2167. return *user, ErrInvalidCredentials
  2168. }
  2169. if user.Password == "" {
  2170. return *user, errors.New("credentials cannot be null or empty")
  2171. }
  2172. if !user.Filters.Hooks.CheckPasswordDisabled {
  2173. hookResponse, err := executeCheckPasswordHook(user.Username, password, ip, protocol)
  2174. if err != nil {
  2175. providerLog(logger.LevelDebug, "error executing check password hook for user %#v, ip %v, protocol %v: %v",
  2176. user.Username, ip, protocol, err)
  2177. return *user, errors.New("unable to check credentials")
  2178. }
  2179. switch hookResponse.Status {
  2180. case -1:
  2181. // no hook configured
  2182. case 1:
  2183. providerLog(logger.LevelDebug, "password accepted by check password hook for user %#v, ip %v, protocol %v",
  2184. user.Username, ip, protocol)
  2185. return *user, nil
  2186. case 2:
  2187. providerLog(logger.LevelDebug, "partial success from check password hook for user %#v, ip %v, protocol %v",
  2188. user.Username, ip, protocol)
  2189. password = hookResponse.ToVerify
  2190. default:
  2191. providerLog(logger.LevelDebug, "password rejected by check password hook for user %#v, ip %v, protocol %v, status: %v",
  2192. user.Username, ip, protocol, hookResponse.Status)
  2193. return *user, ErrInvalidCredentials
  2194. }
  2195. }
  2196. match, err := isPasswordOK(user, password)
  2197. if !match {
  2198. err = ErrInvalidCredentials
  2199. }
  2200. return *user, err
  2201. }
  2202. func checkUserPasscode(user *User, password, protocol string) (string, error) {
  2203. if user.Filters.TOTPConfig.Enabled {
  2204. switch protocol {
  2205. case protocolFTP:
  2206. if util.IsStringInSlice(protocol, user.Filters.TOTPConfig.Protocols) {
  2207. // the TOTP passcode has six digits
  2208. pwdLen := len(password)
  2209. if pwdLen < 7 {
  2210. providerLog(logger.LevelDebug, "password len %v is too short to contain a passcode, user %#v, protocol %v",
  2211. pwdLen, user.Username, protocol)
  2212. return "", util.NewValidationError("password too short, cannot contain the passcode")
  2213. }
  2214. err := user.Filters.TOTPConfig.Secret.TryDecrypt()
  2215. if err != nil {
  2216. providerLog(logger.LevelError, "unable to decrypt TOTP secret for user %#v, protocol %v, err: %v",
  2217. user.Username, protocol, err)
  2218. return "", err
  2219. }
  2220. pwd := password[0:(pwdLen - 6)]
  2221. passcode := password[(pwdLen - 6):]
  2222. match, err := mfa.ValidateTOTPPasscode(user.Filters.TOTPConfig.ConfigName, passcode,
  2223. user.Filters.TOTPConfig.Secret.GetPayload())
  2224. if !match || err != nil {
  2225. providerLog(logger.LevelWarn, "invalid passcode for user %#v, protocol %v, err: %v",
  2226. user.Username, protocol, err)
  2227. return "", util.NewValidationError("invalid passcode")
  2228. }
  2229. return pwd, nil
  2230. }
  2231. }
  2232. }
  2233. return password, nil
  2234. }
  2235. func checkUserAndPubKey(user *User, pubKey []byte, isSSHCert bool) (User, string, error) {
  2236. err := user.CheckLoginConditions()
  2237. if err != nil {
  2238. return *user, "", err
  2239. }
  2240. if isSSHCert {
  2241. return *user, "", nil
  2242. }
  2243. if len(user.PublicKeys) == 0 {
  2244. return *user, "", ErrInvalidCredentials
  2245. }
  2246. for i, k := range user.PublicKeys {
  2247. storedPubKey, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
  2248. if err != nil {
  2249. providerLog(logger.LevelError, "error parsing stored public key %d for user %s: %v", i, user.Username, err)
  2250. return *user, "", err
  2251. }
  2252. if bytes.Equal(storedPubKey.Marshal(), pubKey) {
  2253. return *user, fmt.Sprintf("%s:%s", ssh.FingerprintSHA256(storedPubKey), comment), nil
  2254. }
  2255. }
  2256. return *user, "", ErrInvalidCredentials
  2257. }
  2258. func compareUnixPasswordAndHash(user *User, password string) (bool, error) {
  2259. var crypter crypt.Crypter
  2260. if strings.HasPrefix(user.Password, sha512cryptPwdPrefix) {
  2261. crypter = sha512_crypt.New()
  2262. } else if strings.HasPrefix(user.Password, md5cryptPwdPrefix) {
  2263. crypter = md5_crypt.New()
  2264. } else if strings.HasPrefix(user.Password, md5cryptApr1PwdPrefix) {
  2265. crypter = apr1_crypt.New()
  2266. } else {
  2267. return false, errors.New("unix crypt: invalid or unsupported hash format")
  2268. }
  2269. if err := crypter.Verify(user.Password, []byte(password)); err != nil {
  2270. return false, err
  2271. }
  2272. return true, nil
  2273. }
  2274. func comparePbkdf2PasswordAndHash(password, hashedPassword string) (bool, error) {
  2275. vals := strings.Split(hashedPassword, "$")
  2276. if len(vals) != 5 {
  2277. return false, fmt.Errorf("pbkdf2: hash is not in the correct format")
  2278. }
  2279. iterations, err := strconv.Atoi(vals[2])
  2280. if err != nil {
  2281. return false, err
  2282. }
  2283. expected, err := base64.StdEncoding.DecodeString(vals[4])
  2284. if err != nil {
  2285. return false, err
  2286. }
  2287. var salt []byte
  2288. if util.IsStringPrefixInSlice(hashedPassword, pbkdfPwdB64SaltPrefixes) {
  2289. salt, err = base64.StdEncoding.DecodeString(vals[3])
  2290. if err != nil {
  2291. return false, err
  2292. }
  2293. } else {
  2294. salt = []byte(vals[3])
  2295. }
  2296. var hashFunc func() hash.Hash
  2297. if strings.HasPrefix(hashedPassword, pbkdf2SHA256Prefix) || strings.HasPrefix(hashedPassword, pbkdf2SHA256B64SaltPrefix) {
  2298. hashFunc = sha256.New
  2299. } else if strings.HasPrefix(hashedPassword, pbkdf2SHA512Prefix) {
  2300. hashFunc = sha512.New
  2301. } else if strings.HasPrefix(hashedPassword, pbkdf2SHA1Prefix) {
  2302. hashFunc = sha1.New
  2303. } else {
  2304. return false, fmt.Errorf("pbkdf2: invalid or unsupported hash format %v", vals[1])
  2305. }
  2306. df := pbkdf2.Key([]byte(password), salt, iterations, len(expected), hashFunc)
  2307. return subtle.ConstantTimeCompare(df, expected) == 1, nil
  2308. }
  2309. func addCredentialsToUser(user *User) error {
  2310. if err := addFolderCredentialsToUser(user); err != nil {
  2311. return err
  2312. }
  2313. if user.FsConfig.Provider != sdk.GCSFilesystemProvider {
  2314. return nil
  2315. }
  2316. if user.FsConfig.GCSConfig.AutomaticCredentials > 0 {
  2317. return nil
  2318. }
  2319. // Don't read from file if credentials have already been set
  2320. if user.FsConfig.GCSConfig.Credentials.IsValid() {
  2321. return nil
  2322. }
  2323. cred, err := os.ReadFile(user.GetGCSCredentialsFilePath())
  2324. if err != nil {
  2325. return err
  2326. }
  2327. return json.Unmarshal(cred, &user.FsConfig.GCSConfig.Credentials)
  2328. }
  2329. func addFolderCredentialsToUser(user *User) error {
  2330. for idx := range user.VirtualFolders {
  2331. f := &user.VirtualFolders[idx]
  2332. if f.FsConfig.Provider != sdk.GCSFilesystemProvider {
  2333. continue
  2334. }
  2335. if f.FsConfig.GCSConfig.AutomaticCredentials > 0 {
  2336. continue
  2337. }
  2338. // Don't read from file if credentials have already been set
  2339. if f.FsConfig.GCSConfig.Credentials.IsValid() {
  2340. continue
  2341. }
  2342. cred, err := os.ReadFile(f.GetGCSCredentialsFilePath())
  2343. if err != nil {
  2344. return err
  2345. }
  2346. err = json.Unmarshal(cred, f.FsConfig.GCSConfig.Credentials)
  2347. if err != nil {
  2348. return err
  2349. }
  2350. }
  2351. return nil
  2352. }
  2353. func getSSLMode() string {
  2354. if config.Driver == PGSQLDataProviderName || config.Driver == CockroachDataProviderName {
  2355. switch config.SSLMode {
  2356. case 0:
  2357. return "disable"
  2358. case 1:
  2359. return "require"
  2360. case 2:
  2361. return "verify-ca"
  2362. case 3:
  2363. return "verify-full"
  2364. }
  2365. } else if config.Driver == MySQLDataProviderName {
  2366. if config.requireCustomTLSForMySQL() {
  2367. return "custom"
  2368. }
  2369. switch config.SSLMode {
  2370. case 0:
  2371. return "false"
  2372. case 1:
  2373. return "true"
  2374. case 2:
  2375. return "skip-verify"
  2376. case 3:
  2377. return "preferred"
  2378. }
  2379. }
  2380. return ""
  2381. }
  2382. func terminateInteractiveAuthProgram(cmd *exec.Cmd, isFinished bool) {
  2383. if isFinished {
  2384. return
  2385. }
  2386. providerLog(logger.LevelInfo, "kill interactive auth program after an unexpected error")
  2387. err := cmd.Process.Kill()
  2388. if err != nil {
  2389. providerLog(logger.LevelDebug, "error killing interactive auth program: %v", err)
  2390. }
  2391. }
  2392. func sendKeyboardAuthHTTPReq(url string, request *plugin.KeyboardAuthRequest) (*plugin.KeyboardAuthResponse, error) {
  2393. reqAsJSON, err := json.Marshal(request)
  2394. if err != nil {
  2395. providerLog(logger.LevelError, "error serializing keyboard interactive auth request: %v", err)
  2396. return nil, err
  2397. }
  2398. resp, err := httpclient.Post(url, "application/json", bytes.NewBuffer(reqAsJSON))
  2399. if err != nil {
  2400. providerLog(logger.LevelError, "error getting keyboard interactive auth hook HTTP response: %v", err)
  2401. return nil, err
  2402. }
  2403. defer resp.Body.Close()
  2404. if resp.StatusCode != http.StatusOK {
  2405. return nil, fmt.Errorf("wrong keyboard interactive auth http status code: %v, expected 200", resp.StatusCode)
  2406. }
  2407. var response plugin.KeyboardAuthResponse
  2408. err = render.DecodeJSON(resp.Body, &response)
  2409. return &response, err
  2410. }
  2411. func doBuiltinKeyboardInteractiveAuth(user *User, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  2412. answers, err := client("", "", []string{"Password: "}, []bool{false})
  2413. if err != nil {
  2414. return 0, err
  2415. }
  2416. if len(answers) != 1 {
  2417. return 0, fmt.Errorf("unexpected number of answers: %v", len(answers))
  2418. }
  2419. _, err = checkUserAndPass(user, answers[0], ip, protocol)
  2420. if err != nil {
  2421. return 0, err
  2422. }
  2423. if !user.Filters.TOTPConfig.Enabled || !util.IsStringInSlice(protocolSSH, user.Filters.TOTPConfig.Protocols) {
  2424. return 1, nil
  2425. }
  2426. err = user.Filters.TOTPConfig.Secret.TryDecrypt()
  2427. if err != nil {
  2428. providerLog(logger.LevelError, "unable to decrypt TOTP secret for user %#v, protocol %v, err: %v",
  2429. user.Username, protocol, err)
  2430. return 0, err
  2431. }
  2432. answers, err = client("", "", []string{"Authentication code: "}, []bool{false})
  2433. if err != nil {
  2434. return 0, err
  2435. }
  2436. if len(answers) != 1 {
  2437. return 0, fmt.Errorf("unexpected number of answers: %v", len(answers))
  2438. }
  2439. match, err := mfa.ValidateTOTPPasscode(user.Filters.TOTPConfig.ConfigName, answers[0],
  2440. user.Filters.TOTPConfig.Secret.GetPayload())
  2441. if !match || err != nil {
  2442. providerLog(logger.LevelWarn, "invalid passcode for user %#v, protocol %v, err: %v",
  2443. user.Username, protocol, err)
  2444. return 0, util.NewValidationError("invalid passcode")
  2445. }
  2446. return 1, nil
  2447. }
  2448. func executeKeyboardInteractivePlugin(user *User, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  2449. authResult := 0
  2450. requestID := xid.New().String()
  2451. authStep := 1
  2452. req := &plugin.KeyboardAuthRequest{
  2453. Username: user.Username,
  2454. IP: ip,
  2455. Password: user.Password,
  2456. RequestID: requestID,
  2457. Step: authStep,
  2458. }
  2459. var response *plugin.KeyboardAuthResponse
  2460. var err error
  2461. for {
  2462. response, err = plugin.Handler.ExecuteKeyboardInteractiveStep(req)
  2463. if err != nil {
  2464. return authResult, err
  2465. }
  2466. if response.AuthResult != 0 {
  2467. return response.AuthResult, err
  2468. }
  2469. if err = response.Validate(); err != nil {
  2470. providerLog(logger.LevelInfo, "invalid response from keyboard interactive plugin: %v", err)
  2471. return authResult, err
  2472. }
  2473. answers, err := getKeyboardInteractiveAnswers(client, response, user, ip, protocol)
  2474. if err != nil {
  2475. return authResult, err
  2476. }
  2477. authStep++
  2478. req = &plugin.KeyboardAuthRequest{
  2479. RequestID: requestID,
  2480. Step: authStep,
  2481. Username: user.Username,
  2482. Password: user.Password,
  2483. Answers: answers,
  2484. Questions: response.Questions,
  2485. }
  2486. }
  2487. }
  2488. func executeKeyboardInteractiveHTTPHook(user *User, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  2489. authResult := 0
  2490. requestID := xid.New().String()
  2491. authStep := 1
  2492. req := &plugin.KeyboardAuthRequest{
  2493. Username: user.Username,
  2494. IP: ip,
  2495. Password: user.Password,
  2496. RequestID: requestID,
  2497. Step: authStep,
  2498. }
  2499. var response *plugin.KeyboardAuthResponse
  2500. var err error
  2501. for {
  2502. response, err = sendKeyboardAuthHTTPReq(authHook, req)
  2503. if err != nil {
  2504. return authResult, err
  2505. }
  2506. if response.AuthResult != 0 {
  2507. return response.AuthResult, err
  2508. }
  2509. if err = response.Validate(); err != nil {
  2510. providerLog(logger.LevelInfo, "invalid response from keyboard interactive http hook: %v", err)
  2511. return authResult, err
  2512. }
  2513. answers, err := getKeyboardInteractiveAnswers(client, response, user, ip, protocol)
  2514. if err != nil {
  2515. return authResult, err
  2516. }
  2517. authStep++
  2518. req = &plugin.KeyboardAuthRequest{
  2519. RequestID: requestID,
  2520. Step: authStep,
  2521. Username: user.Username,
  2522. Password: user.Password,
  2523. Answers: answers,
  2524. Questions: response.Questions,
  2525. }
  2526. }
  2527. }
  2528. func getKeyboardInteractiveAnswers(client ssh.KeyboardInteractiveChallenge, response *plugin.KeyboardAuthResponse,
  2529. user *User, ip, protocol string,
  2530. ) ([]string, error) {
  2531. questions := response.Questions
  2532. answers, err := client("", response.Instruction, questions, response.Echos)
  2533. if err != nil {
  2534. providerLog(logger.LevelInfo, "error getting interactive auth client response: %v", err)
  2535. return answers, err
  2536. }
  2537. if len(answers) != len(questions) {
  2538. err = fmt.Errorf("client answers does not match questions, expected: %v actual: %v", questions, answers)
  2539. providerLog(logger.LevelInfo, "keyboard interactive auth error: %v", err)
  2540. return answers, err
  2541. }
  2542. if len(answers) == 1 && response.CheckPwd > 0 {
  2543. if response.CheckPwd == 2 {
  2544. if !user.Filters.TOTPConfig.Enabled || !util.IsStringInSlice(protocolSSH, user.Filters.TOTPConfig.Protocols) {
  2545. providerLog(logger.LevelInfo, "keyboard interactive auth error: unable to check TOTP passcode, TOTP is not enabled for user %#v",
  2546. user.Username)
  2547. return answers, errors.New("TOTP not enabled for SSH protocol")
  2548. }
  2549. err := user.Filters.TOTPConfig.Secret.TryDecrypt()
  2550. if err != nil {
  2551. providerLog(logger.LevelError, "unable to decrypt TOTP secret for user %#v, protocol %v, err: %v",
  2552. user.Username, protocol, err)
  2553. return answers, fmt.Errorf("unable to decrypt TOTP secret: %w", err)
  2554. }
  2555. match, err := mfa.ValidateTOTPPasscode(user.Filters.TOTPConfig.ConfigName, answers[0],
  2556. user.Filters.TOTPConfig.Secret.GetPayload())
  2557. if !match || err != nil {
  2558. providerLog(logger.LevelInfo, "keyboard interactive auth error: unable to validate passcode for user %#v, match? %v, err: %v",
  2559. user.Username, match, err)
  2560. return answers, errors.New("unable to validate TOTP passcode")
  2561. }
  2562. } else {
  2563. _, err = checkUserAndPass(user, answers[0], ip, protocol)
  2564. providerLog(logger.LevelInfo, "interactive auth hook requested password validation for user %#v, validation error: %v",
  2565. user.Username, err)
  2566. if err != nil {
  2567. return answers, err
  2568. }
  2569. }
  2570. answers[0] = "OK"
  2571. }
  2572. return answers, err
  2573. }
  2574. func handleProgramInteractiveQuestions(client ssh.KeyboardInteractiveChallenge, response *plugin.KeyboardAuthResponse,
  2575. user *User, stdin io.WriteCloser, ip, protocol string,
  2576. ) error {
  2577. answers, err := getKeyboardInteractiveAnswers(client, response, user, ip, protocol)
  2578. if err != nil {
  2579. return err
  2580. }
  2581. for _, answer := range answers {
  2582. if runtime.GOOS == "windows" {
  2583. answer += "\r"
  2584. }
  2585. answer += "\n"
  2586. _, err = stdin.Write([]byte(answer))
  2587. if err != nil {
  2588. providerLog(logger.LevelError, "unable to write client answer to keyboard interactive program: %v", err)
  2589. return err
  2590. }
  2591. }
  2592. return nil
  2593. }
  2594. func executeKeyboardInteractiveProgram(user *User, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (int, error) {
  2595. authResult := 0
  2596. ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
  2597. defer cancel()
  2598. cmd := exec.CommandContext(ctx, authHook)
  2599. cmd.Env = append(os.Environ(),
  2600. fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", user.Username),
  2601. fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
  2602. fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", user.Password))
  2603. stdout, err := cmd.StdoutPipe()
  2604. if err != nil {
  2605. return authResult, err
  2606. }
  2607. stdin, err := cmd.StdinPipe()
  2608. if err != nil {
  2609. return authResult, err
  2610. }
  2611. err = cmd.Start()
  2612. if err != nil {
  2613. return authResult, err
  2614. }
  2615. var once sync.Once
  2616. scanner := bufio.NewScanner(stdout)
  2617. for scanner.Scan() {
  2618. var response plugin.KeyboardAuthResponse
  2619. err = json.Unmarshal(scanner.Bytes(), &response)
  2620. if err != nil {
  2621. providerLog(logger.LevelInfo, "interactive auth error parsing response: %v", err)
  2622. once.Do(func() { terminateInteractiveAuthProgram(cmd, false) })
  2623. break
  2624. }
  2625. if response.AuthResult != 0 {
  2626. authResult = response.AuthResult
  2627. break
  2628. }
  2629. if err = response.Validate(); err != nil {
  2630. providerLog(logger.LevelInfo, "invalid response from keyboard interactive program: %v", err)
  2631. once.Do(func() { terminateInteractiveAuthProgram(cmd, false) })
  2632. break
  2633. }
  2634. go func() {
  2635. err := handleProgramInteractiveQuestions(client, &response, user, stdin, ip, protocol)
  2636. if err != nil {
  2637. once.Do(func() { terminateInteractiveAuthProgram(cmd, false) })
  2638. }
  2639. }()
  2640. }
  2641. stdin.Close()
  2642. once.Do(func() { terminateInteractiveAuthProgram(cmd, true) })
  2643. go func() {
  2644. _, err := cmd.Process.Wait()
  2645. if err != nil {
  2646. providerLog(logger.LevelWarn, "error waiting for #%v process to exit: %v", authHook, err)
  2647. }
  2648. }()
  2649. return authResult, err
  2650. }
  2651. func doKeyboardInteractiveAuth(user *User, authHook string, client ssh.KeyboardInteractiveChallenge, ip, protocol string) (User, error) {
  2652. var authResult int
  2653. var err error
  2654. if plugin.Handler.HasAuthScope(plugin.AuthScopeKeyboardInteractive) {
  2655. authResult, err = executeKeyboardInteractivePlugin(user, client, ip, protocol)
  2656. } else if authHook != "" {
  2657. if strings.HasPrefix(authHook, "http") {
  2658. authResult, err = executeKeyboardInteractiveHTTPHook(user, authHook, client, ip, protocol)
  2659. } else {
  2660. authResult, err = executeKeyboardInteractiveProgram(user, authHook, client, ip, protocol)
  2661. }
  2662. } else {
  2663. authResult, err = doBuiltinKeyboardInteractiveAuth(user, client, ip, protocol)
  2664. }
  2665. if err != nil {
  2666. return *user, err
  2667. }
  2668. if authResult != 1 {
  2669. return *user, fmt.Errorf("keyboard interactive auth failed, result: %v", authResult)
  2670. }
  2671. err = user.CheckLoginConditions()
  2672. if err != nil {
  2673. return *user, err
  2674. }
  2675. return *user, nil
  2676. }
  2677. func isCheckPasswordHookDefined(protocol string) bool {
  2678. if config.CheckPasswordHook == "" {
  2679. return false
  2680. }
  2681. if config.CheckPasswordScope == 0 {
  2682. return true
  2683. }
  2684. switch protocol {
  2685. case protocolSSH:
  2686. return config.CheckPasswordScope&1 != 0
  2687. case protocolFTP:
  2688. return config.CheckPasswordScope&2 != 0
  2689. case protocolWebDAV:
  2690. return config.CheckPasswordScope&4 != 0
  2691. default:
  2692. return false
  2693. }
  2694. }
  2695. func getPasswordHookResponse(username, password, ip, protocol string) ([]byte, error) {
  2696. if strings.HasPrefix(config.CheckPasswordHook, "http") {
  2697. var result []byte
  2698. req := checkPasswordRequest{
  2699. Username: username,
  2700. Password: password,
  2701. IP: ip,
  2702. Protocol: protocol,
  2703. }
  2704. reqAsJSON, err := json.Marshal(req)
  2705. if err != nil {
  2706. return result, err
  2707. }
  2708. resp, err := httpclient.Post(config.CheckPasswordHook, "application/json", bytes.NewBuffer(reqAsJSON))
  2709. if err != nil {
  2710. providerLog(logger.LevelError, "error getting check password hook response: %v", err)
  2711. return result, err
  2712. }
  2713. defer resp.Body.Close()
  2714. if resp.StatusCode != http.StatusOK {
  2715. return result, fmt.Errorf("wrong http status code from chek password hook: %v, expected 200", resp.StatusCode)
  2716. }
  2717. return io.ReadAll(io.LimitReader(resp.Body, maxHookResponseSize))
  2718. }
  2719. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  2720. defer cancel()
  2721. cmd := exec.CommandContext(ctx, config.CheckPasswordHook)
  2722. cmd.Env = append(os.Environ(),
  2723. fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", username),
  2724. fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", password),
  2725. fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
  2726. fmt.Sprintf("SFTPGO_AUTHD_PROTOCOL=%v", protocol),
  2727. )
  2728. return cmd.Output()
  2729. }
  2730. func executeCheckPasswordHook(username, password, ip, protocol string) (checkPasswordResponse, error) {
  2731. var response checkPasswordResponse
  2732. if !isCheckPasswordHookDefined(protocol) {
  2733. response.Status = -1
  2734. return response, nil
  2735. }
  2736. startTime := time.Now()
  2737. out, err := getPasswordHookResponse(username, password, ip, protocol)
  2738. providerLog(logger.LevelDebug, "check password hook executed, error: %v, elapsed: %v", err, time.Since(startTime))
  2739. if err != nil {
  2740. return response, err
  2741. }
  2742. err = json.Unmarshal(out, &response)
  2743. return response, err
  2744. }
  2745. func getPreLoginHookResponse(loginMethod, ip, protocol string, userAsJSON []byte) ([]byte, error) {
  2746. if strings.HasPrefix(config.PreLoginHook, "http") {
  2747. var url *url.URL
  2748. var result []byte
  2749. url, err := url.Parse(config.PreLoginHook)
  2750. if err != nil {
  2751. providerLog(logger.LevelError, "invalid url for pre-login hook %#v, error: %v", config.PreLoginHook, err)
  2752. return result, err
  2753. }
  2754. q := url.Query()
  2755. q.Add("login_method", loginMethod)
  2756. q.Add("ip", ip)
  2757. q.Add("protocol", protocol)
  2758. url.RawQuery = q.Encode()
  2759. resp, err := httpclient.Post(url.String(), "application/json", bytes.NewBuffer(userAsJSON))
  2760. if err != nil {
  2761. providerLog(logger.LevelWarn, "error getting pre-login hook response: %v", err)
  2762. return result, err
  2763. }
  2764. defer resp.Body.Close()
  2765. if resp.StatusCode == http.StatusNoContent {
  2766. return result, nil
  2767. }
  2768. if resp.StatusCode != http.StatusOK {
  2769. return result, fmt.Errorf("wrong pre-login hook http status code: %v, expected 200", resp.StatusCode)
  2770. }
  2771. return io.ReadAll(io.LimitReader(resp.Body, maxHookResponseSize))
  2772. }
  2773. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  2774. defer cancel()
  2775. cmd := exec.CommandContext(ctx, config.PreLoginHook)
  2776. cmd.Env = append(os.Environ(),
  2777. fmt.Sprintf("SFTPGO_LOGIND_USER=%v", string(userAsJSON)),
  2778. fmt.Sprintf("SFTPGO_LOGIND_METHOD=%v", loginMethod),
  2779. fmt.Sprintf("SFTPGO_LOGIND_IP=%v", ip),
  2780. fmt.Sprintf("SFTPGO_LOGIND_PROTOCOL=%v", protocol),
  2781. )
  2782. return cmd.Output()
  2783. }
  2784. func executePreLoginHook(username, loginMethod, ip, protocol string) (User, error) {
  2785. u, userAsJSON, err := getUserAndJSONForHook(username)
  2786. if err != nil {
  2787. return u, err
  2788. }
  2789. if u.Filters.Hooks.PreLoginDisabled {
  2790. return u, nil
  2791. }
  2792. startTime := time.Now()
  2793. out, err := getPreLoginHookResponse(loginMethod, ip, protocol, userAsJSON)
  2794. if err != nil {
  2795. return u, fmt.Errorf("pre-login hook error: %v, username %#v, ip %v, protocol %v elapsed %v",
  2796. err, username, ip, protocol, time.Since(startTime))
  2797. }
  2798. providerLog(logger.LevelDebug, "pre-login hook completed, elapsed: %v", time.Since(startTime))
  2799. if util.IsByteArrayEmpty(out) {
  2800. providerLog(logger.LevelDebug, "empty response from pre-login hook, no modification requested for user %#v id: %v",
  2801. username, u.ID)
  2802. if u.ID == 0 {
  2803. return u, util.NewRecordNotFoundError(fmt.Sprintf("username %#v does not exist", username))
  2804. }
  2805. return u, nil
  2806. }
  2807. userID := u.ID
  2808. userPwd := u.Password
  2809. userUsedQuotaSize := u.UsedQuotaSize
  2810. userUsedQuotaFiles := u.UsedQuotaFiles
  2811. userUsedDownloadTransfer := u.UsedDownloadDataTransfer
  2812. userUsedUploadTransfer := u.UsedUploadDataTransfer
  2813. userLastQuotaUpdate := u.LastQuotaUpdate
  2814. userLastLogin := u.LastLogin
  2815. userCreatedAt := u.CreatedAt
  2816. totpConfig := u.Filters.TOTPConfig
  2817. recoveryCodes := u.Filters.RecoveryCodes
  2818. err = json.Unmarshal(out, &u)
  2819. if err != nil {
  2820. return u, fmt.Errorf("invalid pre-login hook response %#v, error: %v", string(out), err)
  2821. }
  2822. u.ID = userID
  2823. u.UsedQuotaSize = userUsedQuotaSize
  2824. u.UsedQuotaFiles = userUsedQuotaFiles
  2825. u.UsedUploadDataTransfer = userUsedUploadTransfer
  2826. u.UsedDownloadDataTransfer = userUsedDownloadTransfer
  2827. u.LastQuotaUpdate = userLastQuotaUpdate
  2828. u.LastLogin = userLastLogin
  2829. u.CreatedAt = userCreatedAt
  2830. if userID == 0 {
  2831. err = provider.addUser(&u)
  2832. } else {
  2833. u.UpdatedAt = util.GetTimeAsMsSinceEpoch(time.Now())
  2834. // preserve TOTP config and recovery codes
  2835. u.Filters.TOTPConfig = totpConfig
  2836. u.Filters.RecoveryCodes = recoveryCodes
  2837. err = provider.updateUser(&u)
  2838. if err == nil {
  2839. webDAVUsersCache.swap(&u)
  2840. if u.Password != userPwd {
  2841. cachedPasswords.Remove(username)
  2842. }
  2843. }
  2844. }
  2845. if err != nil {
  2846. return u, err
  2847. }
  2848. providerLog(logger.LevelDebug, "user %#v added/updated from pre-login hook response, id: %v", username, userID)
  2849. if userID == 0 {
  2850. return provider.userExists(username)
  2851. }
  2852. return u, nil
  2853. }
  2854. // ExecutePostLoginHook executes the post login hook if defined
  2855. func ExecutePostLoginHook(user *User, loginMethod, ip, protocol string, err error) {
  2856. if config.PostLoginHook == "" {
  2857. return
  2858. }
  2859. if config.PostLoginScope == 1 && err == nil {
  2860. return
  2861. }
  2862. if config.PostLoginScope == 2 && err != nil {
  2863. return
  2864. }
  2865. go func() {
  2866. status := "0"
  2867. if err == nil {
  2868. status = "1"
  2869. }
  2870. user.PrepareForRendering()
  2871. userAsJSON, err := json.Marshal(user)
  2872. if err != nil {
  2873. providerLog(logger.LevelError, "error serializing user in post login hook: %v", err)
  2874. return
  2875. }
  2876. if strings.HasPrefix(config.PostLoginHook, "http") {
  2877. var url *url.URL
  2878. url, err := url.Parse(config.PostLoginHook)
  2879. if err != nil {
  2880. providerLog(logger.LevelDebug, "Invalid post-login hook %#v", config.PostLoginHook)
  2881. return
  2882. }
  2883. q := url.Query()
  2884. q.Add("login_method", loginMethod)
  2885. q.Add("ip", ip)
  2886. q.Add("protocol", protocol)
  2887. q.Add("status", status)
  2888. url.RawQuery = q.Encode()
  2889. startTime := time.Now()
  2890. respCode := 0
  2891. resp, err := httpclient.RetryablePost(url.String(), "application/json", bytes.NewBuffer(userAsJSON))
  2892. if err == nil {
  2893. respCode = resp.StatusCode
  2894. resp.Body.Close()
  2895. }
  2896. providerLog(logger.LevelDebug, "post login hook executed for user %#v, ip %v, protocol %v, response code: %v, elapsed: %v err: %v",
  2897. user.Username, ip, protocol, respCode, time.Since(startTime), err)
  2898. return
  2899. }
  2900. ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
  2901. defer cancel()
  2902. cmd := exec.CommandContext(ctx, config.PostLoginHook)
  2903. cmd.Env = append(os.Environ(),
  2904. fmt.Sprintf("SFTPGO_LOGIND_USER=%v", string(userAsJSON)),
  2905. fmt.Sprintf("SFTPGO_LOGIND_IP=%v", ip),
  2906. fmt.Sprintf("SFTPGO_LOGIND_METHOD=%v", loginMethod),
  2907. fmt.Sprintf("SFTPGO_LOGIND_STATUS=%v", status),
  2908. fmt.Sprintf("SFTPGO_LOGIND_PROTOCOL=%v", protocol))
  2909. startTime := time.Now()
  2910. err = cmd.Run()
  2911. providerLog(logger.LevelDebug, "post login hook executed for user %#v, ip %v, protocol %v, elapsed %v err: %v",
  2912. user.Username, ip, protocol, time.Since(startTime), err)
  2913. }()
  2914. }
  2915. func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol string, cert *x509.Certificate, userAsJSON []byte) ([]byte, error) {
  2916. var tlsCert string
  2917. if cert != nil {
  2918. var err error
  2919. tlsCert, err = util.EncodeTLSCertToPem(cert)
  2920. if err != nil {
  2921. return nil, err
  2922. }
  2923. }
  2924. if strings.HasPrefix(config.ExternalAuthHook, "http") {
  2925. var result []byte
  2926. authRequest := make(map[string]string)
  2927. authRequest["username"] = username
  2928. authRequest["ip"] = ip
  2929. authRequest["password"] = password
  2930. authRequest["public_key"] = pkey
  2931. authRequest["protocol"] = protocol
  2932. authRequest["keyboard_interactive"] = keyboardInteractive
  2933. authRequest["tls_cert"] = tlsCert
  2934. if len(userAsJSON) > 0 {
  2935. authRequest["user"] = string(userAsJSON)
  2936. }
  2937. authRequestAsJSON, err := json.Marshal(authRequest)
  2938. if err != nil {
  2939. providerLog(logger.LevelError, "error serializing external auth request: %v", err)
  2940. return result, err
  2941. }
  2942. resp, err := httpclient.Post(config.ExternalAuthHook, "application/json", bytes.NewBuffer(authRequestAsJSON))
  2943. if err != nil {
  2944. providerLog(logger.LevelWarn, "error getting external auth hook HTTP response: %v", err)
  2945. return result, err
  2946. }
  2947. defer resp.Body.Close()
  2948. providerLog(logger.LevelDebug, "external auth hook executed, response code: %v", resp.StatusCode)
  2949. if resp.StatusCode != http.StatusOK {
  2950. return result, fmt.Errorf("wrong external auth http status code: %v, expected 200", resp.StatusCode)
  2951. }
  2952. return io.ReadAll(io.LimitReader(resp.Body, maxHookResponseSize))
  2953. }
  2954. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  2955. defer cancel()
  2956. cmd := exec.CommandContext(ctx, config.ExternalAuthHook)
  2957. cmd.Env = append(os.Environ(),
  2958. fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", username),
  2959. fmt.Sprintf("SFTPGO_AUTHD_USER=%v", string(userAsJSON)),
  2960. fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
  2961. fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", password),
  2962. fmt.Sprintf("SFTPGO_AUTHD_PUBLIC_KEY=%v", pkey),
  2963. fmt.Sprintf("SFTPGO_AUTHD_PROTOCOL=%v", protocol),
  2964. fmt.Sprintf("SFTPGO_AUTHD_TLS_CERT=%v", strings.ReplaceAll(tlsCert, "\n", "\\n")),
  2965. fmt.Sprintf("SFTPGO_AUTHD_KEYBOARD_INTERACTIVE=%v", keyboardInteractive))
  2966. return cmd.Output()
  2967. }
  2968. func updateUserFromExtAuthResponse(user *User, password, pkey string) {
  2969. if password != "" {
  2970. user.Password = password
  2971. }
  2972. if pkey != "" && !util.IsStringPrefixInSlice(pkey, user.PublicKeys) {
  2973. user.PublicKeys = append(user.PublicKeys, pkey)
  2974. }
  2975. }
  2976. func doExternalAuth(username, password string, pubKey []byte, keyboardInteractive, ip, protocol string,
  2977. tlsCert *x509.Certificate,
  2978. ) (User, error) {
  2979. var user User
  2980. u, userAsJSON, err := getUserAndJSONForHook(username)
  2981. if err != nil {
  2982. return user, err
  2983. }
  2984. if u.Filters.Hooks.ExternalAuthDisabled {
  2985. return u, nil
  2986. }
  2987. if u.isExternalAuthCached() {
  2988. return u, nil
  2989. }
  2990. pkey, err := util.GetSSHPublicKeyAsString(pubKey)
  2991. if err != nil {
  2992. return user, err
  2993. }
  2994. startTime := time.Now()
  2995. out, err := getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol, tlsCert, userAsJSON)
  2996. if err != nil {
  2997. return user, fmt.Errorf("external auth error for user %#v: %v, elapsed: %v", username, err, time.Since(startTime))
  2998. }
  2999. providerLog(logger.LevelDebug, "external auth completed for user %#v, elapsed: %v", username, time.Since(startTime))
  3000. if util.IsByteArrayEmpty(out) {
  3001. providerLog(logger.LevelDebug, "empty response from external hook, no modification requested for user %#v id: %v",
  3002. username, u.ID)
  3003. if u.ID == 0 {
  3004. return u, util.NewRecordNotFoundError(fmt.Sprintf("username %#v does not exist", username))
  3005. }
  3006. return u, nil
  3007. }
  3008. err = json.Unmarshal(out, &user)
  3009. if err != nil {
  3010. return user, fmt.Errorf("invalid external auth response: %v", err)
  3011. }
  3012. // an empty username means authentication failure
  3013. if user.Username == "" {
  3014. return user, ErrInvalidCredentials
  3015. }
  3016. updateUserFromExtAuthResponse(&user, password, pkey)
  3017. // some users want to map multiple login usernames with a single SFTPGo account
  3018. // for example an SFTP user logins using "user1" or "user2" and the external auth
  3019. // returns "user" in both cases, so we use the username returned from
  3020. // external auth and not the one used to login
  3021. if user.Username != username {
  3022. u, err = provider.userExists(user.Username)
  3023. }
  3024. if u.ID > 0 && err == nil {
  3025. user.ID = u.ID
  3026. user.UsedQuotaSize = u.UsedQuotaSize
  3027. user.UsedQuotaFiles = u.UsedQuotaFiles
  3028. user.UsedUploadDataTransfer = u.UsedUploadDataTransfer
  3029. user.UsedDownloadDataTransfer = u.UsedDownloadDataTransfer
  3030. user.LastQuotaUpdate = u.LastQuotaUpdate
  3031. user.LastLogin = u.LastLogin
  3032. user.CreatedAt = u.CreatedAt
  3033. user.UpdatedAt = util.GetTimeAsMsSinceEpoch(time.Now())
  3034. // preserve TOTP config and recovery codes
  3035. user.Filters.TOTPConfig = u.Filters.TOTPConfig
  3036. user.Filters.RecoveryCodes = u.Filters.RecoveryCodes
  3037. err = provider.updateUser(&user)
  3038. if err == nil {
  3039. webDAVUsersCache.swap(&user)
  3040. cachedPasswords.Add(user.Username, password)
  3041. }
  3042. return user, err
  3043. }
  3044. err = provider.addUser(&user)
  3045. if err != nil {
  3046. return user, err
  3047. }
  3048. return provider.userExists(user.Username)
  3049. }
  3050. func doPluginAuth(username, password string, pubKey []byte, ip, protocol string,
  3051. tlsCert *x509.Certificate, authScope int,
  3052. ) (User, error) {
  3053. var user User
  3054. u, userAsJSON, err := getUserAndJSONForHook(username)
  3055. if err != nil {
  3056. return user, err
  3057. }
  3058. if u.Filters.Hooks.ExternalAuthDisabled {
  3059. return u, nil
  3060. }
  3061. if u.isExternalAuthCached() {
  3062. return u, nil
  3063. }
  3064. pkey, err := util.GetSSHPublicKeyAsString(pubKey)
  3065. if err != nil {
  3066. return user, err
  3067. }
  3068. startTime := time.Now()
  3069. out, err := plugin.Handler.Authenticate(username, password, ip, protocol, pkey, tlsCert, authScope, userAsJSON)
  3070. if err != nil {
  3071. return user, fmt.Errorf("plugin auth error for user %#v: %v, elapsed: %v, auth scope: %v",
  3072. username, err, time.Since(startTime), authScope)
  3073. }
  3074. providerLog(logger.LevelDebug, "plugin auth completed for user %#v, elapsed: %v,auth scope: %v",
  3075. username, time.Since(startTime), authScope)
  3076. if util.IsByteArrayEmpty(out) {
  3077. providerLog(logger.LevelDebug, "empty response from plugin auth, no modification requested for user %#v id: %v",
  3078. username, u.ID)
  3079. if u.ID == 0 {
  3080. return u, util.NewRecordNotFoundError(fmt.Sprintf("username %#v does not exist", username))
  3081. }
  3082. return u, nil
  3083. }
  3084. err = json.Unmarshal(out, &user)
  3085. if err != nil {
  3086. return user, fmt.Errorf("invalid plugin auth response: %v", err)
  3087. }
  3088. updateUserFromExtAuthResponse(&user, password, pkey)
  3089. if u.ID > 0 {
  3090. user.ID = u.ID
  3091. user.UsedQuotaSize = u.UsedQuotaSize
  3092. user.UsedQuotaFiles = u.UsedQuotaFiles
  3093. user.UsedUploadDataTransfer = u.UsedUploadDataTransfer
  3094. user.UsedDownloadDataTransfer = u.UsedDownloadDataTransfer
  3095. user.LastQuotaUpdate = u.LastQuotaUpdate
  3096. user.LastLogin = u.LastLogin
  3097. // preserve TOTP config and recovery codes
  3098. user.Filters.TOTPConfig = u.Filters.TOTPConfig
  3099. user.Filters.RecoveryCodes = u.Filters.RecoveryCodes
  3100. err = provider.updateUser(&user)
  3101. if err == nil {
  3102. webDAVUsersCache.swap(&user)
  3103. cachedPasswords.Add(user.Username, password)
  3104. }
  3105. return user, err
  3106. }
  3107. err = provider.addUser(&user)
  3108. if err != nil {
  3109. return user, err
  3110. }
  3111. return provider.userExists(user.Username)
  3112. }
  3113. func getUserAndJSONForHook(username string) (User, []byte, error) {
  3114. var userAsJSON []byte
  3115. u, err := provider.userExists(username)
  3116. if err != nil {
  3117. if _, ok := err.(*util.RecordNotFoundError); !ok {
  3118. return u, userAsJSON, err
  3119. }
  3120. u = User{
  3121. BaseUser: sdk.BaseUser{
  3122. ID: 0,
  3123. Username: username,
  3124. },
  3125. }
  3126. }
  3127. userAsJSON, err = json.Marshal(u)
  3128. if err != nil {
  3129. return u, userAsJSON, err
  3130. }
  3131. return u, userAsJSON, err
  3132. }
  3133. func isLastActivityRecent(lastActivity int64, minDelay time.Duration) bool {
  3134. lastActivityTime := util.GetTimeFromMsecSinceEpoch(lastActivity)
  3135. diff := -time.Until(lastActivityTime)
  3136. if diff < -10*time.Second {
  3137. return false
  3138. }
  3139. return diff < minDelay
  3140. }
  3141. func getConfigPath(name, configDir string) string {
  3142. if !util.IsFileInputValid(name) {
  3143. return ""
  3144. }
  3145. if name != "" && !filepath.IsAbs(name) {
  3146. return filepath.Join(configDir, name)
  3147. }
  3148. return name
  3149. }
  3150. func providerLog(level logger.LogLevel, format string, v ...interface{}) {
  3151. logger.Log(level, logSender, "", format, v...)
  3152. }