internal_test.go 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086
  1. package sftpd
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "net"
  8. "os"
  9. "path/filepath"
  10. "runtime"
  11. "testing"
  12. "time"
  13. "github.com/eikenb/pipeat"
  14. "github.com/pkg/sftp"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. "golang.org/x/crypto/ssh"
  18. "github.com/drakkan/sftpgo/v2/common"
  19. "github.com/drakkan/sftpgo/v2/dataprovider"
  20. "github.com/drakkan/sftpgo/v2/kms"
  21. "github.com/drakkan/sftpgo/v2/sdk"
  22. "github.com/drakkan/sftpgo/v2/util"
  23. "github.com/drakkan/sftpgo/v2/vfs"
  24. )
  25. const osWindows = "windows"
  26. type MockChannel struct {
  27. Buffer *bytes.Buffer
  28. StdErrBuffer *bytes.Buffer
  29. ReadError error
  30. WriteError error
  31. ShortWriteErr bool
  32. }
  33. func (c *MockChannel) Read(data []byte) (int, error) {
  34. if c.ReadError != nil {
  35. return 0, c.ReadError
  36. }
  37. return c.Buffer.Read(data)
  38. }
  39. func (c *MockChannel) Write(data []byte) (int, error) {
  40. if c.WriteError != nil {
  41. return 0, c.WriteError
  42. }
  43. if c.ShortWriteErr {
  44. return 0, nil
  45. }
  46. return c.Buffer.Write(data)
  47. }
  48. func (c *MockChannel) Close() error {
  49. return nil
  50. }
  51. func (c *MockChannel) CloseWrite() error {
  52. return nil
  53. }
  54. func (c *MockChannel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
  55. return true, nil
  56. }
  57. func (c *MockChannel) Stderr() io.ReadWriter {
  58. return c.StdErrBuffer
  59. }
  60. // MockOsFs mockable OsFs
  61. type MockOsFs struct {
  62. vfs.Fs
  63. err error
  64. statErr error
  65. isAtomicUploadSupported bool
  66. }
  67. // Name returns the name for the Fs implementation
  68. func (fs MockOsFs) Name() string {
  69. return "mockOsFs"
  70. }
  71. // IsUploadResumeSupported returns true if resuming uploads is supported
  72. func (MockOsFs) IsUploadResumeSupported() bool {
  73. return false
  74. }
  75. // IsAtomicUploadSupported returns true if atomic upload is supported
  76. func (fs MockOsFs) IsAtomicUploadSupported() bool {
  77. return fs.isAtomicUploadSupported
  78. }
  79. // Stat returns a FileInfo describing the named file
  80. func (fs MockOsFs) Stat(name string) (os.FileInfo, error) {
  81. if fs.statErr != nil {
  82. return nil, fs.statErr
  83. }
  84. return os.Stat(name)
  85. }
  86. // Lstat returns a FileInfo describing the named file
  87. func (fs MockOsFs) Lstat(name string) (os.FileInfo, error) {
  88. if fs.statErr != nil {
  89. return nil, fs.statErr
  90. }
  91. return os.Lstat(name)
  92. }
  93. // Remove removes the named file or (empty) directory.
  94. func (fs MockOsFs) Remove(name string, isDir bool) error {
  95. if fs.err != nil {
  96. return fs.err
  97. }
  98. return os.Remove(name)
  99. }
  100. // Rename renames (moves) source to target
  101. func (fs MockOsFs) Rename(source, target string) error {
  102. if fs.err != nil {
  103. return fs.err
  104. }
  105. return os.Rename(source, target)
  106. }
  107. func newMockOsFs(err, statErr error, atomicUpload bool, connectionID, rootDir string) vfs.Fs {
  108. return &MockOsFs{
  109. Fs: vfs.NewOsFs(connectionID, rootDir, ""),
  110. err: err,
  111. statErr: statErr,
  112. isAtomicUploadSupported: atomicUpload,
  113. }
  114. }
  115. func TestRemoveNonexistentQuotaScan(t *testing.T) {
  116. assert.False(t, common.QuotaScans.RemoveUserQuotaScan("username"))
  117. }
  118. func TestGetOSOpenFlags(t *testing.T) {
  119. var flags sftp.FileOpenFlags
  120. flags.Write = true
  121. flags.Excl = true
  122. osFlags := getOSOpenFlags(flags)
  123. assert.NotEqual(t, 0, osFlags&os.O_WRONLY)
  124. assert.NotEqual(t, 0, osFlags&os.O_EXCL)
  125. flags.Append = true
  126. // append flag should be ignored to allow resume
  127. assert.NotEqual(t, 0, osFlags&os.O_WRONLY)
  128. assert.NotEqual(t, 0, osFlags&os.O_EXCL)
  129. }
  130. func TestUploadResumeInvalidOffset(t *testing.T) {
  131. testfile := "testfile" //nolint:goconst
  132. file, err := os.Create(testfile)
  133. assert.NoError(t, err)
  134. user := dataprovider.User{
  135. BaseUser: sdk.BaseUser{
  136. Username: "testuser",
  137. },
  138. }
  139. fs := vfs.NewOsFs("", os.TempDir(), "")
  140. conn := common.NewBaseConnection("", common.ProtocolSFTP, "", "", user)
  141. baseTransfer := common.NewBaseTransfer(file, conn, nil, file.Name(), file.Name(), testfile, common.TransferUpload, 10, 0, 0, false, fs)
  142. transfer := newTransfer(baseTransfer, nil, nil, nil)
  143. _, err = transfer.WriteAt([]byte("test"), 0)
  144. assert.Error(t, err, "upload with invalid offset must fail")
  145. if assert.Error(t, transfer.ErrTransfer) {
  146. assert.EqualError(t, err, transfer.ErrTransfer.Error())
  147. assert.Contains(t, transfer.ErrTransfer.Error(), "invalid write offset")
  148. }
  149. err = transfer.Close()
  150. if assert.Error(t, err) {
  151. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  152. assert.Contains(t, err.Error(), "invalid write offset")
  153. }
  154. err = os.Remove(testfile)
  155. assert.NoError(t, err)
  156. }
  157. func TestReadWriteErrors(t *testing.T) {
  158. testfile := "testfile"
  159. file, err := os.Create(testfile)
  160. assert.NoError(t, err)
  161. user := dataprovider.User{
  162. BaseUser: sdk.BaseUser{
  163. Username: "testuser",
  164. },
  165. }
  166. fs := vfs.NewOsFs("", os.TempDir(), "")
  167. conn := common.NewBaseConnection("", common.ProtocolSFTP, "", "", user)
  168. baseTransfer := common.NewBaseTransfer(file, conn, nil, file.Name(), file.Name(), testfile, common.TransferDownload, 0, 0, 0, false, fs)
  169. transfer := newTransfer(baseTransfer, nil, nil, nil)
  170. err = file.Close()
  171. assert.NoError(t, err)
  172. _, err = transfer.WriteAt([]byte("test"), 0)
  173. assert.Error(t, err, "writing to closed file must fail")
  174. buf := make([]byte, 32768)
  175. _, err = transfer.ReadAt(buf, 0)
  176. assert.Error(t, err, "reading from a closed file must fail")
  177. err = transfer.Close()
  178. assert.Error(t, err)
  179. r, _, err := pipeat.Pipe()
  180. assert.NoError(t, err)
  181. baseTransfer = common.NewBaseTransfer(nil, conn, nil, file.Name(), file.Name(), testfile, common.TransferDownload, 0, 0, 0, false, fs)
  182. transfer = newTransfer(baseTransfer, nil, r, nil)
  183. err = transfer.Close()
  184. assert.NoError(t, err)
  185. _, err = transfer.ReadAt(buf, 0)
  186. assert.Error(t, err, "reading from a closed pipe must fail")
  187. r, w, err := pipeat.Pipe()
  188. assert.NoError(t, err)
  189. pipeWriter := vfs.NewPipeWriter(w)
  190. baseTransfer = common.NewBaseTransfer(nil, conn, nil, file.Name(), file.Name(), testfile, common.TransferDownload, 0, 0, 0, false, fs)
  191. transfer = newTransfer(baseTransfer, pipeWriter, nil, nil)
  192. err = r.Close()
  193. assert.NoError(t, err)
  194. errFake := fmt.Errorf("fake upload error")
  195. go func() {
  196. time.Sleep(100 * time.Millisecond)
  197. pipeWriter.Done(errFake)
  198. }()
  199. err = transfer.closeIO()
  200. assert.EqualError(t, err, errFake.Error())
  201. _, err = transfer.WriteAt([]byte("test"), 0)
  202. assert.Error(t, err, "writing to closed pipe must fail")
  203. err = transfer.BaseTransfer.Close()
  204. assert.EqualError(t, err, errFake.Error())
  205. err = os.Remove(testfile)
  206. assert.NoError(t, err)
  207. assert.Len(t, conn.GetTransfers(), 0)
  208. }
  209. func TestUnsupportedListOP(t *testing.T) {
  210. conn := common.NewBaseConnection("", common.ProtocolSFTP, "", "", dataprovider.User{})
  211. sftpConn := Connection{
  212. BaseConnection: conn,
  213. }
  214. request := sftp.NewRequest("Unsupported", "")
  215. _, err := sftpConn.Filelist(request)
  216. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  217. }
  218. func TestTransferCancelFn(t *testing.T) {
  219. testfile := "testfile"
  220. file, err := os.Create(testfile)
  221. assert.NoError(t, err)
  222. isCancelled := false
  223. cancelFn := func() {
  224. isCancelled = true
  225. }
  226. user := dataprovider.User{
  227. BaseUser: sdk.BaseUser{
  228. Username: "testuser",
  229. },
  230. }
  231. fs := vfs.NewOsFs("", os.TempDir(), "")
  232. conn := common.NewBaseConnection("", common.ProtocolSFTP, "", "", user)
  233. baseTransfer := common.NewBaseTransfer(file, conn, cancelFn, file.Name(), file.Name(), testfile, common.TransferDownload, 0, 0, 0, false, fs)
  234. transfer := newTransfer(baseTransfer, nil, nil, nil)
  235. errFake := errors.New("fake error, this will trigger cancelFn")
  236. transfer.TransferError(errFake)
  237. err = transfer.Close()
  238. if assert.Error(t, err) {
  239. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  240. assert.Contains(t, err.Error(), errFake.Error())
  241. }
  242. if assert.Error(t, transfer.ErrTransfer) {
  243. assert.EqualError(t, transfer.ErrTransfer, errFake.Error())
  244. }
  245. assert.True(t, isCancelled, "cancelFn not called!")
  246. err = os.Remove(testfile)
  247. assert.NoError(t, err)
  248. }
  249. func TestUploadFiles(t *testing.T) {
  250. common.Config.UploadMode = common.UploadModeAtomic
  251. fs := vfs.NewOsFs("123", os.TempDir(), "")
  252. u := dataprovider.User{}
  253. c := Connection{
  254. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", u),
  255. }
  256. var flags sftp.FileOpenFlags
  257. flags.Write = true
  258. flags.Trunc = true
  259. _, err := c.handleSFTPUploadToExistingFile(fs, flags, "missing_path", "other_missing_path", 0, "/missing_path", nil)
  260. assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
  261. common.Config.UploadMode = common.UploadModeStandard
  262. _, err = c.handleSFTPUploadToExistingFile(fs, flags, "missing_path", "other_missing_path", 0, "/missing_path", nil)
  263. assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
  264. missingFile := "missing/relative/file.txt"
  265. if runtime.GOOS == osWindows {
  266. missingFile = "missing\\relative\\file.txt"
  267. }
  268. _, err = c.handleSFTPUploadToNewFile(fs, ".", missingFile, "/missing", nil)
  269. assert.Error(t, err, "upload new file in missing path must fail")
  270. fs = newMockOsFs(nil, nil, false, "123", os.TempDir())
  271. f, err := os.CreateTemp("", "temp")
  272. assert.NoError(t, err)
  273. err = f.Close()
  274. assert.NoError(t, err)
  275. tr, err := c.handleSFTPUploadToExistingFile(fs, flags, f.Name(), f.Name(), 123, f.Name(), nil)
  276. if assert.NoError(t, err) {
  277. transfer := tr.(*transfer)
  278. transfers := c.GetTransfers()
  279. if assert.Equal(t, 1, len(transfers)) {
  280. assert.Equal(t, transfers[0].ID, transfer.GetID())
  281. assert.Equal(t, int64(123), transfer.InitialSize)
  282. err = transfer.Close()
  283. assert.NoError(t, err)
  284. assert.Equal(t, 0, len(c.GetTransfers()))
  285. }
  286. }
  287. err = os.Remove(f.Name())
  288. assert.NoError(t, err)
  289. common.Config.UploadMode = common.UploadModeAtomicWithResume
  290. }
  291. func TestWithInvalidHome(t *testing.T) {
  292. u := dataprovider.User{}
  293. u.HomeDir = "home_rel_path" //nolint:goconst
  294. _, err := loginUser(&u, dataprovider.LoginMethodPassword, "", nil)
  295. assert.Error(t, err, "login a user with an invalid home_dir must fail")
  296. u.HomeDir = os.TempDir()
  297. fs, err := u.GetFilesystem("123")
  298. assert.NoError(t, err)
  299. c := Connection{
  300. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", u),
  301. }
  302. _, err = fs.ResolvePath("../upper_path")
  303. assert.Error(t, err, "tested path is not a home subdir")
  304. _, err = c.StatVFS(&sftp.Request{
  305. Method: "StatVFS",
  306. Filepath: "../unresolvable-path",
  307. })
  308. assert.Error(t, err)
  309. }
  310. func TestResolveWithRootDir(t *testing.T) {
  311. u := dataprovider.User{}
  312. if runtime.GOOS == osWindows {
  313. u.HomeDir = "C:\\"
  314. } else {
  315. u.HomeDir = "/"
  316. }
  317. fs, err := u.GetFilesystem("")
  318. assert.NoError(t, err)
  319. rel, err := filepath.Rel(u.HomeDir, os.TempDir())
  320. assert.NoError(t, err)
  321. p, err := fs.ResolvePath(rel)
  322. assert.NoError(t, err, "path %v", p)
  323. }
  324. func TestSFTPGetUsedQuota(t *testing.T) {
  325. u := dataprovider.User{}
  326. u.HomeDir = "home_rel_path"
  327. u.Username = "test_invalid_user"
  328. u.QuotaSize = 4096
  329. u.QuotaFiles = 1
  330. u.Permissions = make(map[string][]string)
  331. u.Permissions["/"] = []string{dataprovider.PermAny}
  332. connection := Connection{
  333. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", u),
  334. }
  335. quotaResult := connection.HasSpace(false, false, "/")
  336. assert.False(t, quotaResult.HasSpace)
  337. }
  338. func TestSupportedSSHCommands(t *testing.T) {
  339. cmds := GetSupportedSSHCommands()
  340. assert.Equal(t, len(supportedSSHCommands), len(cmds))
  341. for _, c := range cmds {
  342. assert.True(t, util.IsStringInSlice(c, supportedSSHCommands))
  343. }
  344. }
  345. func TestSSHCommandPath(t *testing.T) {
  346. buf := make([]byte, 65535)
  347. stdErrBuf := make([]byte, 65535)
  348. mockSSHChannel := MockChannel{
  349. Buffer: bytes.NewBuffer(buf),
  350. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  351. ReadError: nil,
  352. }
  353. connection := &Connection{
  354. channel: &mockSSHChannel,
  355. }
  356. sshCommand := sshCommand{
  357. command: "test",
  358. connection: connection,
  359. args: []string{},
  360. }
  361. assert.Equal(t, "", sshCommand.getDestPath())
  362. sshCommand.args = []string{"-t", "/tmp/../path"}
  363. assert.Equal(t, "/path", sshCommand.getDestPath())
  364. sshCommand.args = []string{"-t", "/tmp/"}
  365. assert.Equal(t, "/tmp/", sshCommand.getDestPath())
  366. sshCommand.args = []string{"-t", "tmp/"}
  367. assert.Equal(t, "/tmp/", sshCommand.getDestPath())
  368. sshCommand.args = []string{"-t", "/tmp/../../../path"}
  369. assert.Equal(t, "/path", sshCommand.getDestPath())
  370. sshCommand.args = []string{"-t", ".."}
  371. assert.Equal(t, "/", sshCommand.getDestPath())
  372. sshCommand.args = []string{"-t", "."}
  373. assert.Equal(t, "/", sshCommand.getDestPath())
  374. sshCommand.args = []string{"-t", "//"}
  375. assert.Equal(t, "/", sshCommand.getDestPath())
  376. sshCommand.args = []string{"-t", "../.."}
  377. assert.Equal(t, "/", sshCommand.getDestPath())
  378. sshCommand.args = []string{"-t", "/.."}
  379. assert.Equal(t, "/", sshCommand.getDestPath())
  380. sshCommand.args = []string{"-f", "/a space.txt"}
  381. assert.Equal(t, "/a space.txt", sshCommand.getDestPath())
  382. }
  383. func TestSSHParseCommandPayload(t *testing.T) {
  384. cmd := "command -a -f /ab\\ à/some\\ spaces\\ \\ \\(\\).txt"
  385. name, args, _ := parseCommandPayload(cmd)
  386. assert.Equal(t, "command", name)
  387. assert.Equal(t, 3, len(args))
  388. assert.Equal(t, "/ab à/some spaces ().txt", args[2])
  389. _, _, err := parseCommandPayload("")
  390. assert.Error(t, err, "parsing invalid command must fail")
  391. }
  392. func TestSSHCommandErrors(t *testing.T) {
  393. buf := make([]byte, 65535)
  394. stdErrBuf := make([]byte, 65535)
  395. readErr := fmt.Errorf("test read error")
  396. mockSSHChannel := MockChannel{
  397. Buffer: bytes.NewBuffer(buf),
  398. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  399. ReadError: readErr,
  400. }
  401. server, client := net.Pipe()
  402. defer func() {
  403. err := server.Close()
  404. assert.NoError(t, err)
  405. }()
  406. defer func() {
  407. err := client.Close()
  408. assert.NoError(t, err)
  409. }()
  410. user := dataprovider.User{}
  411. user.Permissions = make(map[string][]string)
  412. user.Permissions["/"] = []string{dataprovider.PermAny}
  413. connection := Connection{
  414. BaseConnection: common.NewBaseConnection("", common.ProtocolSSH, "", "", user),
  415. channel: &mockSSHChannel,
  416. }
  417. cmd := sshCommand{
  418. command: "md5sum",
  419. connection: &connection,
  420. args: []string{},
  421. }
  422. err := cmd.handle()
  423. assert.Error(t, err, "ssh command must fail, we are sending a fake error")
  424. cmd = sshCommand{
  425. command: "md5sum",
  426. connection: &connection,
  427. args: []string{"/../../test_file_ftp.dat"},
  428. }
  429. err = cmd.handle()
  430. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  431. cmd = sshCommand{
  432. command: "git-receive-pack",
  433. connection: &connection,
  434. args: []string{"/../../testrepo"},
  435. }
  436. err = cmd.handle()
  437. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  438. cmd.connection.User.HomeDir = filepath.Clean(os.TempDir())
  439. cmd.connection.User.QuotaFiles = 1
  440. cmd.connection.User.UsedQuotaFiles = 2
  441. fs, err := cmd.connection.User.GetFilesystem("123")
  442. assert.NoError(t, err)
  443. err = cmd.handle()
  444. assert.EqualError(t, err, common.ErrQuotaExceeded.Error())
  445. cmd.connection.User.QuotaFiles = 0
  446. cmd.connection.User.UsedQuotaFiles = 0
  447. cmd.connection.User.Permissions = make(map[string][]string)
  448. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermListItems}
  449. err = cmd.handle()
  450. assert.EqualError(t, err, common.ErrPermissionDenied.Error())
  451. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
  452. cmd.command = "invalid_command"
  453. command, err := cmd.getSystemCommand()
  454. assert.NoError(t, err)
  455. err = cmd.executeSystemCommand(command)
  456. assert.Error(t, err, "invalid command must fail")
  457. command, err = cmd.getSystemCommand()
  458. assert.NoError(t, err)
  459. _, err = command.cmd.StderrPipe()
  460. assert.NoError(t, err)
  461. err = cmd.executeSystemCommand(command)
  462. assert.Error(t, err, "command must fail, pipe was already assigned")
  463. err = cmd.executeSystemCommand(command)
  464. assert.Error(t, err, "command must fail, pipe was already assigned")
  465. command, err = cmd.getSystemCommand()
  466. assert.NoError(t, err)
  467. _, err = command.cmd.StdoutPipe()
  468. assert.NoError(t, err)
  469. err = cmd.executeSystemCommand(command)
  470. assert.Error(t, err, "command must fail, pipe was already assigned")
  471. cmd = sshCommand{
  472. command: "sftpgo-remove",
  473. connection: &connection,
  474. args: []string{"/../../src"},
  475. }
  476. err = cmd.handle()
  477. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  478. cmd = sshCommand{
  479. command: "sftpgo-copy",
  480. connection: &connection,
  481. args: []string{"/../../test_src", "."},
  482. }
  483. err = cmd.handle()
  484. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  485. cmd.connection.User.HomeDir = filepath.Clean(os.TempDir())
  486. cmd = sshCommand{
  487. command: "sftpgo-copy",
  488. connection: &connection,
  489. args: []string{"src", "dst"},
  490. }
  491. cmd.connection.User.Permissions = make(map[string][]string)
  492. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
  493. if runtime.GOOS != osWindows {
  494. aDir := filepath.Join(os.TempDir(), "adir")
  495. err = os.MkdirAll(aDir, os.ModePerm)
  496. assert.NoError(t, err)
  497. tmpFile := filepath.Join(aDir, "testcopy")
  498. err = os.WriteFile(tmpFile, []byte("aaa"), os.ModePerm)
  499. assert.NoError(t, err)
  500. err = os.Chmod(aDir, 0001)
  501. assert.NoError(t, err)
  502. err = cmd.checkCopyDestination(fs, tmpFile)
  503. assert.Error(t, err)
  504. err = os.Chmod(aDir, os.ModePerm)
  505. assert.NoError(t, err)
  506. err = os.Remove(tmpFile)
  507. assert.NoError(t, err)
  508. }
  509. }
  510. func TestCommandsWithExtensionsFilter(t *testing.T) {
  511. buf := make([]byte, 65535)
  512. stdErrBuf := make([]byte, 65535)
  513. mockSSHChannel := MockChannel{
  514. Buffer: bytes.NewBuffer(buf),
  515. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  516. }
  517. server, client := net.Pipe()
  518. defer server.Close()
  519. defer client.Close()
  520. user := dataprovider.User{
  521. BaseUser: sdk.BaseUser{
  522. Username: "test",
  523. HomeDir: os.TempDir(),
  524. Status: 1,
  525. },
  526. }
  527. user.Filters.FilePatterns = []sdk.PatternsFilter{
  528. {
  529. Path: "/subdir",
  530. AllowedPatterns: []string{".jpg"},
  531. DeniedPatterns: []string{},
  532. },
  533. }
  534. connection := &Connection{
  535. BaseConnection: common.NewBaseConnection("", common.ProtocolSSH, "", "", user),
  536. channel: &mockSSHChannel,
  537. }
  538. cmd := sshCommand{
  539. command: "md5sum",
  540. connection: connection,
  541. args: []string{"subdir/test.png"},
  542. }
  543. err := cmd.handleHashCommands()
  544. assert.EqualError(t, err, common.ErrPermissionDenied.Error())
  545. cmd = sshCommand{
  546. command: "rsync",
  547. connection: connection,
  548. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  549. }
  550. _, err = cmd.getSystemCommand()
  551. assert.EqualError(t, err, errUnsupportedConfig.Error())
  552. cmd = sshCommand{
  553. command: "git-receive-pack",
  554. connection: connection,
  555. args: []string{"/subdir"},
  556. }
  557. _, err = cmd.getSystemCommand()
  558. assert.EqualError(t, err, errUnsupportedConfig.Error())
  559. cmd = sshCommand{
  560. command: "git-receive-pack",
  561. connection: connection,
  562. args: []string{"/subdir/dir"},
  563. }
  564. _, err = cmd.getSystemCommand()
  565. assert.EqualError(t, err, errUnsupportedConfig.Error())
  566. cmd = sshCommand{
  567. command: "git-receive-pack",
  568. connection: connection,
  569. args: []string{"/adir/subdir"},
  570. }
  571. _, err = cmd.getSystemCommand()
  572. assert.NoError(t, err)
  573. }
  574. func TestSSHCommandsRemoteFs(t *testing.T) {
  575. buf := make([]byte, 65535)
  576. stdErrBuf := make([]byte, 65535)
  577. mockSSHChannel := MockChannel{
  578. Buffer: bytes.NewBuffer(buf),
  579. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  580. }
  581. user := dataprovider.User{}
  582. user.FsConfig = vfs.Filesystem{
  583. Provider: sdk.S3FilesystemProvider,
  584. S3Config: vfs.S3FsConfig{
  585. BaseS3FsConfig: sdk.BaseS3FsConfig{
  586. Bucket: "s3bucket",
  587. Endpoint: "endpoint",
  588. Region: "eu-west-1",
  589. },
  590. },
  591. }
  592. connection := &Connection{
  593. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  594. channel: &mockSSHChannel,
  595. }
  596. cmd := sshCommand{
  597. command: "md5sum",
  598. connection: connection,
  599. args: []string{},
  600. }
  601. command, err := cmd.getSystemCommand()
  602. assert.NoError(t, err)
  603. err = cmd.executeSystemCommand(command)
  604. assert.Error(t, err, "command must fail for a non local filesystem")
  605. cmd = sshCommand{
  606. command: "sftpgo-copy",
  607. connection: connection,
  608. args: []string{},
  609. }
  610. err = cmd.handleSFTPGoCopy()
  611. assert.Error(t, err)
  612. cmd = sshCommand{
  613. command: "sftpgo-remove",
  614. connection: connection,
  615. args: []string{},
  616. }
  617. err = cmd.handleSFTPGoRemove()
  618. assert.Error(t, err)
  619. // the user has no permissions
  620. assert.False(t, cmd.hasCopyPermissions("", "", nil))
  621. }
  622. func TestSSHCmdGetFsErrors(t *testing.T) {
  623. buf := make([]byte, 65535)
  624. stdErrBuf := make([]byte, 65535)
  625. mockSSHChannel := MockChannel{
  626. Buffer: bytes.NewBuffer(buf),
  627. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  628. }
  629. user := dataprovider.User{
  630. BaseUser: sdk.BaseUser{
  631. HomeDir: "relative path",
  632. },
  633. }
  634. user.Permissions = map[string][]string{}
  635. user.Permissions["/"] = []string{dataprovider.PermAny}
  636. connection := &Connection{
  637. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  638. channel: &mockSSHChannel,
  639. }
  640. cmd := sshCommand{
  641. command: "sftpgo-remove",
  642. connection: connection,
  643. args: []string{"path"},
  644. }
  645. err := cmd.handleSFTPGoRemove()
  646. assert.Error(t, err)
  647. cmd = sshCommand{
  648. command: "sftpgo-copy",
  649. connection: connection,
  650. args: []string{"path1", "path2"},
  651. }
  652. _, _, _, _, _, _, err = cmd.getFsAndCopyPaths() //nolint:dogsled
  653. assert.Error(t, err)
  654. user = dataprovider.User{}
  655. user.HomeDir = filepath.Join(os.TempDir(), "home")
  656. user.VirtualFolders = append(connection.User.VirtualFolders, vfs.VirtualFolder{
  657. BaseVirtualFolder: vfs.BaseVirtualFolder{
  658. MappedPath: "relative",
  659. },
  660. VirtualPath: "/vpath",
  661. })
  662. connection.User = user
  663. err = os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  664. assert.NoError(t, err)
  665. cmd = sshCommand{
  666. command: "sftpgo-copy",
  667. connection: connection,
  668. args: []string{"path1", "/vpath/path2"},
  669. }
  670. _, _, _, _, _, _, err = cmd.getFsAndCopyPaths() //nolint:dogsled
  671. assert.Error(t, err)
  672. err = os.Remove(user.GetHomeDir())
  673. assert.NoError(t, err)
  674. }
  675. func TestGitVirtualFolders(t *testing.T) {
  676. permissions := make(map[string][]string)
  677. permissions["/"] = []string{dataprovider.PermAny}
  678. user := dataprovider.User{
  679. BaseUser: sdk.BaseUser{
  680. Permissions: permissions,
  681. HomeDir: os.TempDir(),
  682. },
  683. }
  684. conn := &Connection{
  685. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  686. }
  687. cmd := sshCommand{
  688. command: "git-receive-pack",
  689. connection: conn,
  690. args: []string{"/vdir"},
  691. }
  692. cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  693. BaseVirtualFolder: vfs.BaseVirtualFolder{
  694. MappedPath: os.TempDir(),
  695. },
  696. VirtualPath: "/vdir",
  697. })
  698. _, err := cmd.getSystemCommand()
  699. assert.NoError(t, err)
  700. cmd.args = []string{"/"}
  701. _, err = cmd.getSystemCommand()
  702. assert.EqualError(t, err, errUnsupportedConfig.Error())
  703. cmd.args = []string{"/vdir1"}
  704. _, err = cmd.getSystemCommand()
  705. assert.NoError(t, err)
  706. cmd.connection.User.VirtualFolders = nil
  707. cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  708. BaseVirtualFolder: vfs.BaseVirtualFolder{
  709. MappedPath: os.TempDir(),
  710. },
  711. VirtualPath: "/vdir",
  712. })
  713. cmd.args = []string{"/vdir/subdir"}
  714. _, err = cmd.getSystemCommand()
  715. assert.NoError(t, err)
  716. cmd.args = []string{"/adir/subdir"}
  717. _, err = cmd.getSystemCommand()
  718. assert.NoError(t, err)
  719. }
  720. func TestRsyncOptions(t *testing.T) {
  721. permissions := make(map[string][]string)
  722. permissions["/"] = []string{dataprovider.PermAny}
  723. user := dataprovider.User{
  724. BaseUser: sdk.BaseUser{
  725. Permissions: permissions,
  726. HomeDir: os.TempDir(),
  727. },
  728. }
  729. conn := &Connection{
  730. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  731. }
  732. sshCmd := sshCommand{
  733. command: "rsync",
  734. connection: conn,
  735. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  736. }
  737. cmd, err := sshCmd.getSystemCommand()
  738. assert.NoError(t, err)
  739. assert.True(t, util.IsStringInSlice("--safe-links", cmd.cmd.Args),
  740. "--safe-links must be added if the user has the create symlinks permission")
  741. permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermCreateDirs,
  742. dataprovider.PermListItems, dataprovider.PermOverwrite, dataprovider.PermDelete, dataprovider.PermRename}
  743. user.Permissions = permissions
  744. conn = &Connection{
  745. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  746. }
  747. sshCmd = sshCommand{
  748. command: "rsync",
  749. connection: conn,
  750. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  751. }
  752. cmd, err = sshCmd.getSystemCommand()
  753. assert.NoError(t, err)
  754. assert.True(t, util.IsStringInSlice("--munge-links", cmd.cmd.Args),
  755. "--munge-links must be added if the user has the create symlinks permission")
  756. sshCmd.connection.User.VirtualFolders = append(sshCmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  757. BaseVirtualFolder: vfs.BaseVirtualFolder{
  758. MappedPath: os.TempDir(),
  759. },
  760. VirtualPath: "/vdir",
  761. })
  762. _, err = sshCmd.getSystemCommand()
  763. assert.EqualError(t, err, errUnsupportedConfig.Error())
  764. }
  765. func TestSystemCommandSizeForPath(t *testing.T) {
  766. permissions := make(map[string][]string)
  767. permissions["/"] = []string{dataprovider.PermAny}
  768. user := dataprovider.User{
  769. BaseUser: sdk.BaseUser{
  770. Permissions: permissions,
  771. HomeDir: os.TempDir(),
  772. },
  773. }
  774. fs, err := user.GetFilesystem("123")
  775. assert.NoError(t, err)
  776. conn := &Connection{
  777. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  778. }
  779. sshCmd := sshCommand{
  780. command: "rsync",
  781. connection: conn,
  782. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  783. }
  784. _, _, err = sshCmd.getSizeForPath(fs, "missing path")
  785. assert.NoError(t, err)
  786. testDir := filepath.Join(os.TempDir(), "dir")
  787. err = os.MkdirAll(testDir, os.ModePerm)
  788. assert.NoError(t, err)
  789. testFile := filepath.Join(testDir, "testfile")
  790. err = os.WriteFile(testFile, []byte("test content"), os.ModePerm)
  791. assert.NoError(t, err)
  792. err = os.Symlink(testFile, testFile+".link")
  793. assert.NoError(t, err)
  794. numFiles, size, err := sshCmd.getSizeForPath(fs, testFile+".link")
  795. assert.NoError(t, err)
  796. assert.Equal(t, 0, numFiles)
  797. assert.Equal(t, int64(0), size)
  798. numFiles, size, err = sshCmd.getSizeForPath(fs, testFile)
  799. assert.NoError(t, err)
  800. assert.Equal(t, 1, numFiles)
  801. assert.Equal(t, int64(12), size)
  802. if runtime.GOOS != osWindows {
  803. err = os.Chmod(testDir, 0001)
  804. assert.NoError(t, err)
  805. _, _, err = sshCmd.getSizeForPath(fs, testFile)
  806. assert.Error(t, err)
  807. err = os.Chmod(testDir, os.ModePerm)
  808. assert.NoError(t, err)
  809. }
  810. err = os.RemoveAll(testDir)
  811. assert.NoError(t, err)
  812. }
  813. func TestSystemCommandErrors(t *testing.T) {
  814. buf := make([]byte, 65535)
  815. stdErrBuf := make([]byte, 65535)
  816. readErr := fmt.Errorf("test read error")
  817. writeErr := fmt.Errorf("test write error")
  818. mockSSHChannel := MockChannel{
  819. Buffer: bytes.NewBuffer(buf),
  820. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  821. ReadError: nil,
  822. WriteError: writeErr,
  823. }
  824. permissions := make(map[string][]string)
  825. permissions["/"] = []string{dataprovider.PermAny}
  826. homeDir := filepath.Join(os.TempDir(), "adir")
  827. err := os.MkdirAll(homeDir, os.ModePerm)
  828. assert.NoError(t, err)
  829. err = os.WriteFile(filepath.Join(homeDir, "afile"), []byte("content"), os.ModePerm)
  830. assert.NoError(t, err)
  831. user := dataprovider.User{
  832. BaseUser: sdk.BaseUser{
  833. Permissions: permissions,
  834. HomeDir: homeDir,
  835. },
  836. }
  837. fs, err := user.GetFilesystem("123")
  838. assert.NoError(t, err)
  839. connection := &Connection{
  840. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  841. channel: &mockSSHChannel,
  842. }
  843. var sshCmd sshCommand
  844. if runtime.GOOS == osWindows {
  845. sshCmd = sshCommand{
  846. command: "dir",
  847. connection: connection,
  848. args: []string{"/"},
  849. }
  850. } else {
  851. sshCmd = sshCommand{
  852. command: "ls",
  853. connection: connection,
  854. args: []string{"/"},
  855. }
  856. }
  857. systemCmd, err := sshCmd.getSystemCommand()
  858. assert.NoError(t, err)
  859. systemCmd.cmd.Dir = os.TempDir()
  860. // FIXME: the command completes but the fake client is unable to read the response
  861. // no error is reported in this case. We can see that the expected code is executed
  862. // reading the test coverage
  863. sshCmd.executeSystemCommand(systemCmd) //nolint:errcheck
  864. mockSSHChannel = MockChannel{
  865. Buffer: bytes.NewBuffer(buf),
  866. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  867. ReadError: readErr,
  868. WriteError: nil,
  869. }
  870. sshCmd.connection.channel = &mockSSHChannel
  871. baseTransfer := common.NewBaseTransfer(nil, sshCmd.connection.BaseConnection, nil, "", "", "", common.TransferDownload,
  872. 0, 0, 0, false, fs)
  873. transfer := newTransfer(baseTransfer, nil, nil, nil)
  874. destBuff := make([]byte, 65535)
  875. dst := bytes.NewBuffer(destBuff)
  876. _, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
  877. assert.EqualError(t, err, readErr.Error())
  878. mockSSHChannel = MockChannel{
  879. Buffer: bytes.NewBuffer(buf),
  880. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  881. ReadError: nil,
  882. WriteError: nil,
  883. }
  884. sshCmd.connection.channel = &mockSSHChannel
  885. transfer.MaxWriteSize = 1
  886. _, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
  887. assert.EqualError(t, err, common.ErrQuotaExceeded.Error())
  888. mockSSHChannel = MockChannel{
  889. Buffer: bytes.NewBuffer(buf),
  890. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  891. ReadError: nil,
  892. WriteError: nil,
  893. ShortWriteErr: true,
  894. }
  895. sshCmd.connection.channel = &mockSSHChannel
  896. _, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
  897. assert.EqualError(t, err, io.ErrShortWrite.Error())
  898. transfer.MaxWriteSize = -1
  899. _, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
  900. assert.EqualError(t, err, common.ErrQuotaExceeded.Error())
  901. err = os.RemoveAll(homeDir)
  902. assert.NoError(t, err)
  903. }
  904. func TestCommandGetFsError(t *testing.T) {
  905. user := dataprovider.User{
  906. FsConfig: vfs.Filesystem{
  907. Provider: sdk.CryptedFilesystemProvider,
  908. },
  909. }
  910. conn := &Connection{
  911. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  912. }
  913. sshCmd := sshCommand{
  914. command: "rsync",
  915. connection: conn,
  916. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  917. }
  918. _, err := sshCmd.getSystemCommand()
  919. assert.Error(t, err)
  920. buf := make([]byte, 65535)
  921. stdErrBuf := make([]byte, 65535)
  922. mockSSHChannel := MockChannel{
  923. Buffer: bytes.NewBuffer(buf),
  924. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  925. ReadError: nil,
  926. }
  927. conn = &Connection{
  928. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", user),
  929. channel: &mockSSHChannel,
  930. }
  931. scpCommand := scpCommand{
  932. sshCommand: sshCommand{
  933. command: "scp",
  934. connection: conn,
  935. args: []string{"-t", "/tmp"},
  936. },
  937. }
  938. err = scpCommand.handleRecursiveUpload()
  939. assert.Error(t, err)
  940. err = scpCommand.handleDownload("")
  941. assert.Error(t, err)
  942. }
  943. func TestGetConnectionInfo(t *testing.T) {
  944. c := common.ConnectionStatus{
  945. Username: "test_user",
  946. ConnectionID: "123",
  947. ClientVersion: "client",
  948. RemoteAddress: "127.0.0.1:1234",
  949. Protocol: common.ProtocolSSH,
  950. Command: "sha1sum /test_file_ftp.dat",
  951. }
  952. info := c.GetConnectionInfo()
  953. assert.Contains(t, info, "sha1sum /test_file_ftp.dat")
  954. }
  955. func TestSCPFileMode(t *testing.T) {
  956. mode := getFileModeAsString(0, true)
  957. assert.Equal(t, "0755", mode)
  958. mode = getFileModeAsString(0700, true)
  959. assert.Equal(t, "0700", mode)
  960. mode = getFileModeAsString(0750, true)
  961. assert.Equal(t, "0750", mode)
  962. mode = getFileModeAsString(0777, true)
  963. assert.Equal(t, "0777", mode)
  964. mode = getFileModeAsString(0640, false)
  965. assert.Equal(t, "0640", mode)
  966. mode = getFileModeAsString(0600, false)
  967. assert.Equal(t, "0600", mode)
  968. mode = getFileModeAsString(0, false)
  969. assert.Equal(t, "0644", mode)
  970. fileMode := uint32(0777)
  971. fileMode = fileMode | uint32(os.ModeSetgid)
  972. fileMode = fileMode | uint32(os.ModeSetuid)
  973. fileMode = fileMode | uint32(os.ModeSticky)
  974. mode = getFileModeAsString(os.FileMode(fileMode), false)
  975. assert.Equal(t, "7777", mode)
  976. fileMode = uint32(0644)
  977. fileMode = fileMode | uint32(os.ModeSetgid)
  978. mode = getFileModeAsString(os.FileMode(fileMode), false)
  979. assert.Equal(t, "4644", mode)
  980. fileMode = uint32(0600)
  981. fileMode = fileMode | uint32(os.ModeSetuid)
  982. mode = getFileModeAsString(os.FileMode(fileMode), false)
  983. assert.Equal(t, "2600", mode)
  984. fileMode = uint32(0044)
  985. fileMode = fileMode | uint32(os.ModeSticky)
  986. mode = getFileModeAsString(os.FileMode(fileMode), false)
  987. assert.Equal(t, "1044", mode)
  988. }
  989. func TestSCPUploadError(t *testing.T) {
  990. buf := make([]byte, 65535)
  991. stdErrBuf := make([]byte, 65535)
  992. writeErr := fmt.Errorf("test write error")
  993. mockSSHChannel := MockChannel{
  994. Buffer: bytes.NewBuffer(buf),
  995. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  996. ReadError: nil,
  997. WriteError: writeErr,
  998. }
  999. user := dataprovider.User{
  1000. BaseUser: sdk.BaseUser{
  1001. HomeDir: filepath.Join(os.TempDir()),
  1002. Permissions: make(map[string][]string),
  1003. },
  1004. }
  1005. user.Permissions["/"] = []string{dataprovider.PermAny}
  1006. connection := &Connection{
  1007. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  1008. channel: &mockSSHChannel,
  1009. }
  1010. scpCommand := scpCommand{
  1011. sshCommand: sshCommand{
  1012. command: "scp",
  1013. connection: connection,
  1014. args: []string{"-t", "/"},
  1015. },
  1016. }
  1017. err := scpCommand.handle()
  1018. assert.EqualError(t, err, writeErr.Error())
  1019. mockSSHChannel = MockChannel{
  1020. Buffer: bytes.NewBuffer([]byte("D0755 0 testdir\n")),
  1021. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1022. ReadError: nil,
  1023. WriteError: writeErr,
  1024. }
  1025. err = scpCommand.handleRecursiveUpload()
  1026. assert.EqualError(t, err, writeErr.Error())
  1027. mockSSHChannel = MockChannel{
  1028. Buffer: bytes.NewBuffer([]byte("D0755 a testdir\n")),
  1029. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1030. ReadError: nil,
  1031. WriteError: nil,
  1032. }
  1033. err = scpCommand.handleRecursiveUpload()
  1034. assert.Error(t, err)
  1035. }
  1036. func TestSCPInvalidEndDir(t *testing.T) {
  1037. stdErrBuf := make([]byte, 65535)
  1038. mockSSHChannel := MockChannel{
  1039. Buffer: bytes.NewBuffer([]byte("E\n")),
  1040. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1041. }
  1042. connection := &Connection{
  1043. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", dataprovider.User{
  1044. BaseUser: sdk.BaseUser{
  1045. HomeDir: os.TempDir(),
  1046. },
  1047. }),
  1048. channel: &mockSSHChannel,
  1049. }
  1050. scpCommand := scpCommand{
  1051. sshCommand: sshCommand{
  1052. command: "scp",
  1053. connection: connection,
  1054. args: []string{"-t", "/tmp"},
  1055. },
  1056. }
  1057. err := scpCommand.handleRecursiveUpload()
  1058. assert.EqualError(t, err, "unacceptable end dir command")
  1059. }
  1060. func TestSCPParseUploadMessage(t *testing.T) {
  1061. buf := make([]byte, 65535)
  1062. stdErrBuf := make([]byte, 65535)
  1063. mockSSHChannel := MockChannel{
  1064. Buffer: bytes.NewBuffer(buf),
  1065. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1066. ReadError: nil,
  1067. }
  1068. fs := vfs.NewOsFs("", os.TempDir(), "")
  1069. connection := &Connection{
  1070. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", dataprovider.User{
  1071. BaseUser: sdk.BaseUser{
  1072. HomeDir: os.TempDir(),
  1073. },
  1074. }),
  1075. channel: &mockSSHChannel,
  1076. }
  1077. scpCommand := scpCommand{
  1078. sshCommand: sshCommand{
  1079. command: "scp",
  1080. connection: connection,
  1081. args: []string{"-t", "/tmp"},
  1082. },
  1083. }
  1084. _, _, err := scpCommand.parseUploadMessage(fs, "invalid")
  1085. assert.Error(t, err, "parsing invalid upload message must fail")
  1086. _, _, err = scpCommand.parseUploadMessage(fs, "D0755 0")
  1087. assert.Error(t, err, "parsing incomplete upload message must fail")
  1088. _, _, err = scpCommand.parseUploadMessage(fs, "D0755 invalidsize testdir")
  1089. assert.Error(t, err, "parsing upload message with invalid size must fail")
  1090. _, _, err = scpCommand.parseUploadMessage(fs, "D0755 0 ")
  1091. assert.Error(t, err, "parsing upload message with invalid name must fail")
  1092. }
  1093. func TestSCPProtocolMessages(t *testing.T) {
  1094. buf := make([]byte, 65535)
  1095. stdErrBuf := make([]byte, 65535)
  1096. readErr := fmt.Errorf("test read error")
  1097. writeErr := fmt.Errorf("test write error")
  1098. mockSSHChannel := MockChannel{
  1099. Buffer: bytes.NewBuffer(buf),
  1100. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1101. ReadError: readErr,
  1102. WriteError: writeErr,
  1103. }
  1104. connection := &Connection{
  1105. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{}),
  1106. channel: &mockSSHChannel,
  1107. }
  1108. scpCommand := scpCommand{
  1109. sshCommand: sshCommand{
  1110. command: "scp",
  1111. connection: connection,
  1112. args: []string{"-t", "/tmp"},
  1113. },
  1114. }
  1115. _, err := scpCommand.readProtocolMessage()
  1116. assert.EqualError(t, err, readErr.Error())
  1117. err = scpCommand.sendConfirmationMessage()
  1118. assert.EqualError(t, err, writeErr.Error())
  1119. err = scpCommand.sendProtocolMessage("E\n")
  1120. assert.EqualError(t, err, writeErr.Error())
  1121. _, err = scpCommand.getNextUploadProtocolMessage()
  1122. assert.EqualError(t, err, readErr.Error())
  1123. mockSSHChannel = MockChannel{
  1124. Buffer: bytes.NewBuffer([]byte("T1183832947 0 1183833773 0\n")),
  1125. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1126. ReadError: nil,
  1127. WriteError: writeErr,
  1128. }
  1129. scpCommand.connection.channel = &mockSSHChannel
  1130. _, err = scpCommand.getNextUploadProtocolMessage()
  1131. assert.EqualError(t, err, writeErr.Error())
  1132. respBuffer := []byte{0x02}
  1133. protocolErrorMsg := "protocol error msg"
  1134. respBuffer = append(respBuffer, protocolErrorMsg...)
  1135. respBuffer = append(respBuffer, 0x0A)
  1136. mockSSHChannel = MockChannel{
  1137. Buffer: bytes.NewBuffer(respBuffer),
  1138. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1139. ReadError: nil,
  1140. WriteError: nil,
  1141. }
  1142. scpCommand.connection.channel = &mockSSHChannel
  1143. err = scpCommand.readConfirmationMessage()
  1144. if assert.Error(t, err) {
  1145. assert.Equal(t, protocolErrorMsg, err.Error())
  1146. }
  1147. }
  1148. func TestSCPTestDownloadProtocolMessages(t *testing.T) {
  1149. buf := make([]byte, 65535)
  1150. stdErrBuf := make([]byte, 65535)
  1151. readErr := fmt.Errorf("test read error")
  1152. writeErr := fmt.Errorf("test write error")
  1153. mockSSHChannel := MockChannel{
  1154. Buffer: bytes.NewBuffer(buf),
  1155. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1156. ReadError: readErr,
  1157. WriteError: writeErr,
  1158. }
  1159. connection := &Connection{
  1160. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{}),
  1161. channel: &mockSSHChannel,
  1162. }
  1163. scpCommand := scpCommand{
  1164. sshCommand: sshCommand{
  1165. command: "scp",
  1166. connection: connection,
  1167. args: []string{"-f", "-p", "/tmp"},
  1168. },
  1169. }
  1170. path := "testDir"
  1171. err := os.Mkdir(path, os.ModePerm)
  1172. assert.NoError(t, err)
  1173. stat, err := os.Stat(path)
  1174. assert.NoError(t, err)
  1175. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1176. assert.EqualError(t, err, writeErr.Error())
  1177. mockSSHChannel = MockChannel{
  1178. Buffer: bytes.NewBuffer(buf),
  1179. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1180. ReadError: readErr,
  1181. WriteError: nil,
  1182. }
  1183. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1184. assert.EqualError(t, err, readErr.Error())
  1185. mockSSHChannel = MockChannel{
  1186. Buffer: bytes.NewBuffer(buf),
  1187. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1188. ReadError: readErr,
  1189. WriteError: writeErr,
  1190. }
  1191. scpCommand.args = []string{"-f", "/tmp"}
  1192. scpCommand.connection.channel = &mockSSHChannel
  1193. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1194. assert.EqualError(t, err, writeErr.Error())
  1195. mockSSHChannel = MockChannel{
  1196. Buffer: bytes.NewBuffer(buf),
  1197. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1198. ReadError: readErr,
  1199. WriteError: nil,
  1200. }
  1201. scpCommand.connection.channel = &mockSSHChannel
  1202. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1203. assert.EqualError(t, err, readErr.Error())
  1204. err = os.Remove(path)
  1205. assert.NoError(t, err)
  1206. }
  1207. func TestSCPCommandHandleErrors(t *testing.T) {
  1208. buf := make([]byte, 65535)
  1209. stdErrBuf := make([]byte, 65535)
  1210. readErr := fmt.Errorf("test read error")
  1211. writeErr := fmt.Errorf("test write error")
  1212. mockSSHChannel := MockChannel{
  1213. Buffer: bytes.NewBuffer(buf),
  1214. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1215. ReadError: readErr,
  1216. WriteError: writeErr,
  1217. }
  1218. server, client := net.Pipe()
  1219. defer func() {
  1220. err := server.Close()
  1221. assert.NoError(t, err)
  1222. }()
  1223. defer func() {
  1224. err := client.Close()
  1225. assert.NoError(t, err)
  1226. }()
  1227. connection := &Connection{
  1228. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{}),
  1229. channel: &mockSSHChannel,
  1230. }
  1231. scpCommand := scpCommand{
  1232. sshCommand: sshCommand{
  1233. command: "scp",
  1234. connection: connection,
  1235. args: []string{"-f", "/tmp"},
  1236. },
  1237. }
  1238. err := scpCommand.handle()
  1239. assert.EqualError(t, err, readErr.Error())
  1240. scpCommand.args = []string{"-i", "/tmp"}
  1241. err = scpCommand.handle()
  1242. assert.Error(t, err, "invalid scp command must fail")
  1243. }
  1244. func TestSCPErrorsMockFs(t *testing.T) {
  1245. errFake := errors.New("fake error")
  1246. u := dataprovider.User{}
  1247. u.Username = "test"
  1248. u.Permissions = make(map[string][]string)
  1249. u.Permissions["/"] = []string{dataprovider.PermAny}
  1250. u.HomeDir = os.TempDir()
  1251. buf := make([]byte, 65535)
  1252. stdErrBuf := make([]byte, 65535)
  1253. mockSSHChannel := MockChannel{
  1254. Buffer: bytes.NewBuffer(buf),
  1255. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1256. }
  1257. server, client := net.Pipe()
  1258. defer func() {
  1259. err := server.Close()
  1260. assert.NoError(t, err)
  1261. }()
  1262. defer func() {
  1263. err := client.Close()
  1264. assert.NoError(t, err)
  1265. }()
  1266. connection := &Connection{
  1267. channel: &mockSSHChannel,
  1268. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", u),
  1269. }
  1270. scpCommand := scpCommand{
  1271. sshCommand: sshCommand{
  1272. command: "scp",
  1273. connection: connection,
  1274. args: []string{"-r", "-t", "/tmp"},
  1275. },
  1276. }
  1277. testfile := filepath.Join(u.HomeDir, "testfile")
  1278. err := os.WriteFile(testfile, []byte("test"), os.ModePerm)
  1279. assert.NoError(t, err)
  1280. fs := newMockOsFs(errFake, nil, true, "123", os.TempDir())
  1281. err = scpCommand.handleUploadFile(fs, testfile, testfile, 0, false, 4, "/testfile")
  1282. assert.NoError(t, err)
  1283. err = os.Remove(testfile)
  1284. assert.NoError(t, err)
  1285. }
  1286. func TestSCPRecursiveDownloadErrors(t *testing.T) {
  1287. buf := make([]byte, 65535)
  1288. stdErrBuf := make([]byte, 65535)
  1289. readErr := fmt.Errorf("test read error")
  1290. writeErr := fmt.Errorf("test write error")
  1291. mockSSHChannel := MockChannel{
  1292. Buffer: bytes.NewBuffer(buf),
  1293. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1294. ReadError: readErr,
  1295. WriteError: writeErr,
  1296. }
  1297. server, client := net.Pipe()
  1298. defer func() {
  1299. err := server.Close()
  1300. assert.NoError(t, err)
  1301. }()
  1302. defer func() {
  1303. err := client.Close()
  1304. assert.NoError(t, err)
  1305. }()
  1306. fs := vfs.NewOsFs("123", os.TempDir(), "")
  1307. connection := &Connection{
  1308. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{
  1309. BaseUser: sdk.BaseUser{
  1310. HomeDir: os.TempDir(),
  1311. },
  1312. }),
  1313. channel: &mockSSHChannel,
  1314. }
  1315. scpCommand := scpCommand{
  1316. sshCommand: sshCommand{
  1317. command: "scp",
  1318. connection: connection,
  1319. args: []string{"-r", "-f", "/tmp"},
  1320. },
  1321. }
  1322. path := "testDir"
  1323. err := os.Mkdir(path, os.ModePerm)
  1324. assert.NoError(t, err)
  1325. stat, err := os.Stat(path)
  1326. assert.NoError(t, err)
  1327. err = scpCommand.handleRecursiveDownload(fs, "invalid_dir", "invalid_dir", stat)
  1328. assert.EqualError(t, err, writeErr.Error())
  1329. mockSSHChannel = MockChannel{
  1330. Buffer: bytes.NewBuffer(buf),
  1331. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1332. ReadError: nil,
  1333. WriteError: nil,
  1334. }
  1335. scpCommand.connection.channel = &mockSSHChannel
  1336. err = scpCommand.handleRecursiveDownload(fs, "invalid_dir", "invalid_dir", stat)
  1337. assert.Error(t, err, "recursive upload download must fail for a non existing dir")
  1338. err = os.Remove(path)
  1339. assert.NoError(t, err)
  1340. }
  1341. func TestSCPRecursiveUploadErrors(t *testing.T) {
  1342. buf := make([]byte, 65535)
  1343. stdErrBuf := make([]byte, 65535)
  1344. readErr := fmt.Errorf("test read error")
  1345. writeErr := fmt.Errorf("test write error")
  1346. mockSSHChannel := MockChannel{
  1347. Buffer: bytes.NewBuffer(buf),
  1348. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1349. ReadError: readErr,
  1350. WriteError: writeErr,
  1351. }
  1352. connection := &Connection{
  1353. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{}),
  1354. channel: &mockSSHChannel,
  1355. }
  1356. scpCommand := scpCommand{
  1357. sshCommand: sshCommand{
  1358. command: "scp",
  1359. connection: connection,
  1360. args: []string{"-r", "-t", "/tmp"},
  1361. },
  1362. }
  1363. err := scpCommand.handleRecursiveUpload()
  1364. assert.Error(t, err, "recursive upload must fail, we send a fake error message")
  1365. mockSSHChannel = MockChannel{
  1366. Buffer: bytes.NewBuffer(buf),
  1367. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1368. ReadError: readErr,
  1369. WriteError: nil,
  1370. }
  1371. scpCommand.connection.channel = &mockSSHChannel
  1372. err = scpCommand.handleRecursiveUpload()
  1373. assert.Error(t, err, "recursive upload must fail, we send a fake error message")
  1374. }
  1375. func TestSCPCreateDirs(t *testing.T) {
  1376. buf := make([]byte, 65535)
  1377. stdErrBuf := make([]byte, 65535)
  1378. u := dataprovider.User{}
  1379. u.HomeDir = "home_rel_path"
  1380. u.Username = "test"
  1381. u.Permissions = make(map[string][]string)
  1382. u.Permissions["/"] = []string{dataprovider.PermAny}
  1383. mockSSHChannel := MockChannel{
  1384. Buffer: bytes.NewBuffer(buf),
  1385. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1386. ReadError: nil,
  1387. WriteError: nil,
  1388. }
  1389. fs, err := u.GetFilesystem("123")
  1390. assert.NoError(t, err)
  1391. connection := &Connection{
  1392. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", u),
  1393. channel: &mockSSHChannel,
  1394. }
  1395. scpCommand := scpCommand{
  1396. sshCommand: sshCommand{
  1397. command: "scp",
  1398. connection: connection,
  1399. args: []string{"-r", "-t", "/tmp"},
  1400. },
  1401. }
  1402. err = scpCommand.handleCreateDir(fs, "invalid_dir")
  1403. assert.Error(t, err, "create invalid dir must fail")
  1404. }
  1405. func TestSCPDownloadFileData(t *testing.T) {
  1406. testfile := "testfile"
  1407. buf := make([]byte, 65535)
  1408. readErr := fmt.Errorf("test read error")
  1409. writeErr := fmt.Errorf("test write error")
  1410. stdErrBuf := make([]byte, 65535)
  1411. mockSSHChannelReadErr := MockChannel{
  1412. Buffer: bytes.NewBuffer(buf),
  1413. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1414. ReadError: readErr,
  1415. WriteError: nil,
  1416. }
  1417. mockSSHChannelWriteErr := MockChannel{
  1418. Buffer: bytes.NewBuffer(buf),
  1419. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1420. ReadError: nil,
  1421. WriteError: writeErr,
  1422. }
  1423. fs := vfs.NewOsFs("", os.TempDir(), "")
  1424. connection := &Connection{
  1425. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{BaseUser: sdk.BaseUser{HomeDir: os.TempDir()}}),
  1426. channel: &mockSSHChannelReadErr,
  1427. }
  1428. scpCommand := scpCommand{
  1429. sshCommand: sshCommand{
  1430. command: "scp",
  1431. connection: connection,
  1432. args: []string{"-r", "-f", "/tmp"},
  1433. },
  1434. }
  1435. err := os.WriteFile(testfile, []byte("test"), os.ModePerm)
  1436. assert.NoError(t, err)
  1437. stat, err := os.Stat(testfile)
  1438. assert.NoError(t, err)
  1439. err = scpCommand.sendDownloadFileData(fs, testfile, stat, nil)
  1440. assert.EqualError(t, err, readErr.Error())
  1441. scpCommand.connection.channel = &mockSSHChannelWriteErr
  1442. err = scpCommand.sendDownloadFileData(fs, testfile, stat, nil)
  1443. assert.EqualError(t, err, writeErr.Error())
  1444. scpCommand.args = []string{"-r", "-p", "-f", "/tmp"}
  1445. err = scpCommand.sendDownloadFileData(fs, testfile, stat, nil)
  1446. assert.EqualError(t, err, writeErr.Error())
  1447. scpCommand.connection.channel = &mockSSHChannelReadErr
  1448. err = scpCommand.sendDownloadFileData(fs, testfile, stat, nil)
  1449. assert.EqualError(t, err, readErr.Error())
  1450. err = os.Remove(testfile)
  1451. assert.NoError(t, err)
  1452. }
  1453. func TestSCPUploadFiledata(t *testing.T) {
  1454. testfile := "testfile"
  1455. buf := make([]byte, 65535)
  1456. stdErrBuf := make([]byte, 65535)
  1457. readErr := fmt.Errorf("test read error")
  1458. writeErr := fmt.Errorf("test write error")
  1459. mockSSHChannel := MockChannel{
  1460. Buffer: bytes.NewBuffer(buf),
  1461. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1462. ReadError: readErr,
  1463. WriteError: writeErr,
  1464. }
  1465. user := dataprovider.User{
  1466. BaseUser: sdk.BaseUser{
  1467. Username: "testuser",
  1468. },
  1469. }
  1470. fs := vfs.NewOsFs("", os.TempDir(), "")
  1471. connection := &Connection{
  1472. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", user),
  1473. channel: &mockSSHChannel,
  1474. }
  1475. scpCommand := scpCommand{
  1476. sshCommand: sshCommand{
  1477. command: "scp",
  1478. connection: connection,
  1479. args: []string{"-r", "-t", "/tmp"},
  1480. },
  1481. }
  1482. file, err := os.Create(testfile)
  1483. assert.NoError(t, err)
  1484. baseTransfer := common.NewBaseTransfer(file, scpCommand.connection.BaseConnection, nil, file.Name(), file.Name(),
  1485. "/"+testfile, common.TransferDownload, 0, 0, 0, true, fs)
  1486. transfer := newTransfer(baseTransfer, nil, nil, nil)
  1487. err = scpCommand.getUploadFileData(2, transfer)
  1488. assert.Error(t, err, "upload must fail, we send a fake write error message")
  1489. mockSSHChannel = MockChannel{
  1490. Buffer: bytes.NewBuffer(buf),
  1491. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1492. ReadError: readErr,
  1493. WriteError: nil,
  1494. }
  1495. scpCommand.connection.channel = &mockSSHChannel
  1496. file, err = os.Create(testfile)
  1497. assert.NoError(t, err)
  1498. transfer.File = file
  1499. transfer.isFinished = false
  1500. transfer.Connection.AddTransfer(transfer)
  1501. err = scpCommand.getUploadFileData(2, transfer)
  1502. assert.Error(t, err, "upload must fail, we send a fake read error message")
  1503. respBuffer := []byte("12")
  1504. respBuffer = append(respBuffer, 0x02)
  1505. mockSSHChannel = MockChannel{
  1506. Buffer: bytes.NewBuffer(respBuffer),
  1507. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1508. ReadError: nil,
  1509. WriteError: nil,
  1510. }
  1511. scpCommand.connection.channel = &mockSSHChannel
  1512. file, err = os.Create(testfile)
  1513. assert.NoError(t, err)
  1514. baseTransfer.File = file
  1515. transfer = newTransfer(baseTransfer, nil, nil, nil)
  1516. transfer.Connection.AddTransfer(transfer)
  1517. err = scpCommand.getUploadFileData(2, transfer)
  1518. assert.Error(t, err, "upload must fail, we have not enough data to read")
  1519. // the file is already closed so we have an error on trasfer closing
  1520. mockSSHChannel = MockChannel{
  1521. Buffer: bytes.NewBuffer(buf),
  1522. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1523. ReadError: nil,
  1524. WriteError: nil,
  1525. }
  1526. transfer.Connection.AddTransfer(transfer)
  1527. err = scpCommand.getUploadFileData(0, transfer)
  1528. if assert.Error(t, err) {
  1529. assert.EqualError(t, err, common.ErrTransferClosed.Error())
  1530. }
  1531. mockSSHChannel = MockChannel{
  1532. Buffer: bytes.NewBuffer(buf),
  1533. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1534. ReadError: nil,
  1535. WriteError: nil,
  1536. }
  1537. transfer.Connection.AddTransfer(transfer)
  1538. err = scpCommand.getUploadFileData(2, transfer)
  1539. assert.True(t, errors.Is(err, os.ErrClosed))
  1540. err = os.Remove(testfile)
  1541. assert.NoError(t, err)
  1542. }
  1543. func TestUploadError(t *testing.T) {
  1544. common.Config.UploadMode = common.UploadModeAtomic
  1545. user := dataprovider.User{
  1546. BaseUser: sdk.BaseUser{
  1547. Username: "testuser",
  1548. },
  1549. }
  1550. fs := vfs.NewOsFs("", os.TempDir(), "")
  1551. connection := &Connection{
  1552. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", user),
  1553. }
  1554. testfile := "testfile"
  1555. fileTempName := "temptestfile"
  1556. file, err := os.Create(fileTempName)
  1557. assert.NoError(t, err)
  1558. baseTransfer := common.NewBaseTransfer(file, connection.BaseConnection, nil, testfile, file.Name(),
  1559. testfile, common.TransferUpload, 0, 0, 0, true, fs)
  1560. transfer := newTransfer(baseTransfer, nil, nil, nil)
  1561. errFake := errors.New("fake error")
  1562. transfer.TransferError(errFake)
  1563. err = transfer.Close()
  1564. if assert.Error(t, err) {
  1565. assert.EqualError(t, err, common.ErrGenericFailure.Error())
  1566. }
  1567. if assert.Error(t, transfer.ErrTransfer) {
  1568. assert.EqualError(t, transfer.ErrTransfer, errFake.Error())
  1569. }
  1570. assert.Equal(t, int64(0), transfer.BytesReceived)
  1571. assert.NoFileExists(t, testfile)
  1572. assert.NoFileExists(t, fileTempName)
  1573. common.Config.UploadMode = common.UploadModeAtomicWithResume
  1574. }
  1575. func TestTransferFailingReader(t *testing.T) {
  1576. user := dataprovider.User{
  1577. BaseUser: sdk.BaseUser{
  1578. Username: "testuser",
  1579. HomeDir: os.TempDir(),
  1580. },
  1581. FsConfig: vfs.Filesystem{
  1582. Provider: sdk.CryptedFilesystemProvider,
  1583. CryptConfig: vfs.CryptFsConfig{
  1584. Passphrase: kms.NewPlainSecret("crypt secret"),
  1585. },
  1586. },
  1587. }
  1588. user.Permissions = make(map[string][]string)
  1589. user.Permissions["/"] = []string{dataprovider.PermAny}
  1590. fs := newMockOsFs(nil, nil, true, "", os.TempDir())
  1591. connection := &Connection{
  1592. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  1593. }
  1594. request := sftp.NewRequest("Open", "afile.txt")
  1595. request.Flags = 27 // read,write,create,truncate
  1596. transfer, err := connection.handleFilewrite(request)
  1597. require.NoError(t, err)
  1598. buf := make([]byte, 32)
  1599. _, err = transfer.ReadAt(buf, 0)
  1600. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  1601. if c, ok := transfer.(io.Closer); ok {
  1602. err = c.Close()
  1603. assert.NoError(t, err)
  1604. }
  1605. fsPath := filepath.Join(os.TempDir(), "afile.txt")
  1606. r, _, err := pipeat.Pipe()
  1607. assert.NoError(t, err)
  1608. baseTransfer := common.NewBaseTransfer(nil, connection.BaseConnection, nil, fsPath, fsPath, filepath.Base(fsPath), common.TransferUpload, 0, 0, 0, false, fs)
  1609. errRead := errors.New("read is not allowed")
  1610. tr := newTransfer(baseTransfer, nil, r, errRead)
  1611. _, err = tr.ReadAt(buf, 0)
  1612. assert.EqualError(t, err, errRead.Error())
  1613. err = tr.Close()
  1614. assert.NoError(t, err)
  1615. tr = newTransfer(baseTransfer, nil, nil, errRead)
  1616. _, err = tr.ReadAt(buf, 0)
  1617. assert.EqualError(t, err, errRead.Error())
  1618. err = tr.Close()
  1619. assert.NoError(t, err)
  1620. err = os.Remove(fsPath)
  1621. assert.NoError(t, err)
  1622. assert.Len(t, connection.GetTransfers(), 0)
  1623. }
  1624. func TestConnectionStatusStruct(t *testing.T) {
  1625. var transfers []common.ConnectionTransfer
  1626. transferUL := common.ConnectionTransfer{
  1627. OperationType: "upload",
  1628. StartTime: util.GetTimeAsMsSinceEpoch(time.Now()),
  1629. Size: 123,
  1630. VirtualPath: "/test.upload",
  1631. }
  1632. transferDL := common.ConnectionTransfer{
  1633. OperationType: "download",
  1634. StartTime: util.GetTimeAsMsSinceEpoch(time.Now()),
  1635. Size: 123,
  1636. VirtualPath: "/test.download",
  1637. }
  1638. transfers = append(transfers, transferUL)
  1639. transfers = append(transfers, transferDL)
  1640. c := common.ConnectionStatus{
  1641. Username: "test",
  1642. ConnectionID: "123",
  1643. ClientVersion: "fakeClient-1.0.0",
  1644. RemoteAddress: "127.0.0.1:1234",
  1645. ConnectionTime: util.GetTimeAsMsSinceEpoch(time.Now()),
  1646. LastActivity: util.GetTimeAsMsSinceEpoch(time.Now()),
  1647. Protocol: "SFTP",
  1648. Transfers: transfers,
  1649. }
  1650. durationString := c.GetConnectionDuration()
  1651. assert.NotEqual(t, 0, len(durationString))
  1652. transfersString := c.GetTransfersAsString()
  1653. assert.NotEqual(t, 0, len(transfersString))
  1654. connInfo := c.GetConnectionInfo()
  1655. assert.NotEqual(t, 0, len(connInfo))
  1656. }
  1657. func TestLoadHostKeys(t *testing.T) {
  1658. configDir := ".."
  1659. serverConfig := &ssh.ServerConfig{}
  1660. c := Configuration{}
  1661. c.HostKeys = []string{".", "missing file"}
  1662. err := c.checkAndLoadHostKeys(configDir, serverConfig)
  1663. assert.Error(t, err)
  1664. testfile := filepath.Join(os.TempDir(), "invalidkey")
  1665. err = os.WriteFile(testfile, []byte("some bytes"), os.ModePerm)
  1666. assert.NoError(t, err)
  1667. c.HostKeys = []string{testfile}
  1668. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1669. assert.Error(t, err)
  1670. err = os.Remove(testfile)
  1671. assert.NoError(t, err)
  1672. keysDir := filepath.Join(os.TempDir(), "keys")
  1673. err = os.MkdirAll(keysDir, os.ModePerm)
  1674. assert.NoError(t, err)
  1675. rsaKeyName := filepath.Join(keysDir, defaultPrivateRSAKeyName)
  1676. ecdsaKeyName := filepath.Join(keysDir, defaultPrivateECDSAKeyName)
  1677. ed25519KeyName := filepath.Join(keysDir, defaultPrivateEd25519KeyName)
  1678. nonDefaultKeyName := filepath.Join(keysDir, "akey")
  1679. c.HostKeys = []string{nonDefaultKeyName, rsaKeyName, ecdsaKeyName, ed25519KeyName}
  1680. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1681. assert.Error(t, err)
  1682. assert.FileExists(t, rsaKeyName)
  1683. assert.FileExists(t, ecdsaKeyName)
  1684. assert.FileExists(t, ed25519KeyName)
  1685. assert.NoFileExists(t, nonDefaultKeyName)
  1686. err = os.Remove(rsaKeyName)
  1687. assert.NoError(t, err)
  1688. err = os.Remove(ecdsaKeyName)
  1689. assert.NoError(t, err)
  1690. err = os.Remove(ed25519KeyName)
  1691. assert.NoError(t, err)
  1692. if runtime.GOOS != osWindows {
  1693. err = os.Chmod(keysDir, 0551)
  1694. assert.NoError(t, err)
  1695. c.HostKeys = nil
  1696. err = c.checkAndLoadHostKeys(keysDir, serverConfig)
  1697. assert.Error(t, err)
  1698. c.HostKeys = []string{rsaKeyName, ecdsaKeyName}
  1699. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1700. assert.Error(t, err)
  1701. c.HostKeys = []string{ecdsaKeyName, rsaKeyName}
  1702. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1703. assert.Error(t, err)
  1704. c.HostKeys = []string{ed25519KeyName}
  1705. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1706. assert.Error(t, err)
  1707. err = os.Chmod(keysDir, 0755)
  1708. assert.NoError(t, err)
  1709. }
  1710. err = os.RemoveAll(keysDir)
  1711. assert.NoError(t, err)
  1712. }
  1713. func TestCertCheckerInitErrors(t *testing.T) {
  1714. c := Configuration{}
  1715. c.TrustedUserCAKeys = []string{".", "missing file"}
  1716. err := c.initializeCertChecker("")
  1717. assert.Error(t, err)
  1718. testfile := filepath.Join(os.TempDir(), "invalidkey")
  1719. err = os.WriteFile(testfile, []byte("some bytes"), os.ModePerm)
  1720. assert.NoError(t, err)
  1721. c.TrustedUserCAKeys = []string{testfile}
  1722. err = c.initializeCertChecker("")
  1723. assert.Error(t, err)
  1724. err = os.Remove(testfile)
  1725. assert.NoError(t, err)
  1726. }
  1727. func TestRecursiveCopyErrors(t *testing.T) {
  1728. permissions := make(map[string][]string)
  1729. permissions["/"] = []string{dataprovider.PermAny}
  1730. user := dataprovider.User{
  1731. BaseUser: sdk.BaseUser{
  1732. Permissions: permissions,
  1733. HomeDir: os.TempDir(),
  1734. },
  1735. }
  1736. fs, err := user.GetFilesystem("123")
  1737. assert.NoError(t, err)
  1738. conn := &Connection{
  1739. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  1740. }
  1741. sshCmd := sshCommand{
  1742. command: "sftpgo-copy",
  1743. connection: conn,
  1744. args: []string{"adir", "another"},
  1745. }
  1746. // try to copy a missing directory
  1747. err = sshCmd.checkRecursiveCopyPermissions(fs, fs, "adir", "another", "/another")
  1748. assert.Error(t, err)
  1749. }
  1750. func TestSFTPSubSystem(t *testing.T) {
  1751. permissions := make(map[string][]string)
  1752. permissions["/"] = []string{dataprovider.PermAny}
  1753. user := &dataprovider.User{
  1754. BaseUser: sdk.BaseUser{
  1755. Permissions: permissions,
  1756. HomeDir: os.TempDir(),
  1757. },
  1758. }
  1759. user.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
  1760. err := ServeSubSystemConnection(user, "connID", nil, nil)
  1761. assert.Error(t, err)
  1762. user.FsConfig.Provider = sdk.LocalFilesystemProvider
  1763. buf := make([]byte, 0, 4096)
  1764. stdErrBuf := make([]byte, 0, 4096)
  1765. mockSSHChannel := &MockChannel{
  1766. Buffer: bytes.NewBuffer(buf),
  1767. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1768. }
  1769. // this is 327680 and it will result in packet too long error
  1770. _, err = mockSSHChannel.Write([]byte{0x00, 0x05, 0x00, 0x00, 0x00, 0x00})
  1771. assert.NoError(t, err)
  1772. err = ServeSubSystemConnection(user, "id", mockSSHChannel, mockSSHChannel)
  1773. assert.EqualError(t, err, "packet too long")
  1774. subsystemChannel := newSubsystemChannel(mockSSHChannel, mockSSHChannel)
  1775. n, err := subsystemChannel.Write([]byte{0x00})
  1776. assert.NoError(t, err)
  1777. assert.Equal(t, n, 1)
  1778. err = subsystemChannel.Close()
  1779. assert.NoError(t, err)
  1780. }
  1781. func TestRecoverer(t *testing.T) {
  1782. c := Configuration{}
  1783. c.AcceptInboundConnection(nil, nil)
  1784. connID := "connectionID"
  1785. connection := &Connection{
  1786. BaseConnection: common.NewBaseConnection(connID, common.ProtocolSFTP, "", "", dataprovider.User{}),
  1787. }
  1788. c.handleSftpConnection(nil, connection)
  1789. sshCmd := sshCommand{
  1790. command: "cd",
  1791. connection: connection,
  1792. }
  1793. err := sshCmd.handle()
  1794. assert.EqualError(t, err, common.ErrGenericFailure.Error())
  1795. scpCmd := scpCommand{
  1796. sshCommand: sshCommand{
  1797. command: "scp",
  1798. connection: connection,
  1799. },
  1800. }
  1801. err = scpCmd.handle()
  1802. assert.EqualError(t, err, common.ErrGenericFailure.Error())
  1803. assert.Len(t, common.Connections.GetStats(), 0)
  1804. }
  1805. func TestListernerAcceptErrors(t *testing.T) {
  1806. errFake := errors.New("a fake error")
  1807. listener := newFakeListener(errFake)
  1808. c := Configuration{}
  1809. err := c.serve(listener, nil)
  1810. require.EqualError(t, err, errFake.Error())
  1811. err = listener.Close()
  1812. require.NoError(t, err)
  1813. errNetFake := &fakeNetError{error: errFake}
  1814. listener = newFakeListener(errNetFake)
  1815. err = c.serve(listener, nil)
  1816. require.EqualError(t, err, errFake.Error())
  1817. err = listener.Close()
  1818. require.NoError(t, err)
  1819. }
  1820. type fakeNetError struct {
  1821. error
  1822. count int
  1823. }
  1824. func (e *fakeNetError) Timeout() bool {
  1825. return false
  1826. }
  1827. func (e *fakeNetError) Temporary() bool {
  1828. e.count++
  1829. return e.count < 10
  1830. }
  1831. func (e *fakeNetError) Error() string {
  1832. return e.error.Error()
  1833. }
  1834. type fakeListener struct {
  1835. server net.Conn
  1836. client net.Conn
  1837. err error
  1838. }
  1839. func (l *fakeListener) Accept() (net.Conn, error) {
  1840. return l.client, l.err
  1841. }
  1842. func (l *fakeListener) Close() error {
  1843. errClient := l.client.Close()
  1844. errServer := l.server.Close()
  1845. if errServer != nil {
  1846. return errServer
  1847. }
  1848. return errClient
  1849. }
  1850. func (l *fakeListener) Addr() net.Addr {
  1851. return l.server.LocalAddr()
  1852. }
  1853. func newFakeListener(err error) net.Listener {
  1854. server, client := net.Pipe()
  1855. return &fakeListener{
  1856. server: server,
  1857. client: client,
  1858. err: err,
  1859. }
  1860. }
  1861. func TestFolderPrefix(t *testing.T) {
  1862. c := Configuration{
  1863. FolderPrefix: "files",
  1864. }
  1865. c.checkFolderPrefix()
  1866. assert.Equal(t, "/files", c.FolderPrefix)
  1867. c.FolderPrefix = ""
  1868. c.checkFolderPrefix()
  1869. assert.Empty(t, c.FolderPrefix)
  1870. c.FolderPrefix = "/"
  1871. c.checkFolderPrefix()
  1872. assert.Empty(t, c.FolderPrefix)
  1873. c.FolderPrefix = "/."
  1874. c.checkFolderPrefix()
  1875. assert.Empty(t, c.FolderPrefix)
  1876. c.FolderPrefix = "."
  1877. c.checkFolderPrefix()
  1878. assert.Empty(t, c.FolderPrefix)
  1879. c.FolderPrefix = ".."
  1880. c.checkFolderPrefix()
  1881. assert.Empty(t, c.FolderPrefix)
  1882. }