dataprovider.go 113 KB

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