server.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. package sftp
  2. // sftp server counterpart
  3. import (
  4. "encoding"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "path/filepath"
  10. "strconv"
  11. "sync"
  12. "syscall"
  13. "time"
  14. "github.com/pkg/errors"
  15. )
  16. const (
  17. sftpServerWorkerCount = 8
  18. )
  19. // Server is an SSH File Transfer Protocol (sftp) server.
  20. // This is intended to provide the sftp subsystem to an ssh server daemon.
  21. // This implementation currently supports most of sftp server protocol version 3,
  22. // as specified at http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
  23. type Server struct {
  24. serverConn
  25. debugStream io.Writer
  26. readOnly bool
  27. pktChan chan rxPacket
  28. openFiles map[string]*os.File
  29. openFilesLock sync.RWMutex
  30. handleCount int
  31. maxTxPacket uint32
  32. }
  33. func (svr *Server) nextHandle(f *os.File) string {
  34. svr.openFilesLock.Lock()
  35. defer svr.openFilesLock.Unlock()
  36. svr.handleCount++
  37. handle := strconv.Itoa(svr.handleCount)
  38. svr.openFiles[handle] = f
  39. return handle
  40. }
  41. func (svr *Server) closeHandle(handle string) error {
  42. svr.openFilesLock.Lock()
  43. defer svr.openFilesLock.Unlock()
  44. if f, ok := svr.openFiles[handle]; ok {
  45. delete(svr.openFiles, handle)
  46. return f.Close()
  47. }
  48. return syscall.EBADF
  49. }
  50. func (svr *Server) getHandle(handle string) (*os.File, bool) {
  51. svr.openFilesLock.RLock()
  52. defer svr.openFilesLock.RUnlock()
  53. f, ok := svr.openFiles[handle]
  54. return f, ok
  55. }
  56. type serverRespondablePacket interface {
  57. encoding.BinaryUnmarshaler
  58. id() uint32
  59. respond(svr *Server) error
  60. }
  61. // NewServer creates a new Server instance around the provided streams, serving
  62. // content from the root of the filesystem. Optionally, ServerOption
  63. // functions may be specified to further configure the Server.
  64. //
  65. // A subsequent call to Serve() is required to begin serving files over SFTP.
  66. func NewServer(rwc io.ReadWriteCloser, options ...ServerOption) (*Server, error) {
  67. s := &Server{
  68. serverConn: serverConn{
  69. conn: conn{
  70. Reader: rwc,
  71. WriteCloser: rwc,
  72. },
  73. },
  74. debugStream: ioutil.Discard,
  75. pktChan: make(chan rxPacket, sftpServerWorkerCount),
  76. openFiles: make(map[string]*os.File),
  77. maxTxPacket: 1 << 15,
  78. }
  79. for _, o := range options {
  80. if err := o(s); err != nil {
  81. return nil, err
  82. }
  83. }
  84. return s, nil
  85. }
  86. // A ServerOption is a function which applies configuration to a Server.
  87. type ServerOption func(*Server) error
  88. // WithDebug enables Server debugging output to the supplied io.Writer.
  89. func WithDebug(w io.Writer) ServerOption {
  90. return func(s *Server) error {
  91. s.debugStream = w
  92. return nil
  93. }
  94. }
  95. // ReadOnly configures a Server to serve files in read-only mode.
  96. func ReadOnly() ServerOption {
  97. return func(s *Server) error {
  98. s.readOnly = true
  99. return nil
  100. }
  101. }
  102. type rxPacket struct {
  103. pktType fxp
  104. pktBytes []byte
  105. }
  106. // Up to N parallel servers
  107. func (svr *Server) sftpServerWorker() error {
  108. for p := range svr.pktChan {
  109. var pkt interface {
  110. encoding.BinaryUnmarshaler
  111. id() uint32
  112. }
  113. var readonly = true
  114. switch p.pktType {
  115. case ssh_FXP_INIT:
  116. pkt = &sshFxInitPacket{}
  117. case ssh_FXP_LSTAT:
  118. pkt = &sshFxpLstatPacket{}
  119. case ssh_FXP_OPEN:
  120. pkt = &sshFxpOpenPacket{}
  121. // readonly handled specially below
  122. case ssh_FXP_CLOSE:
  123. pkt = &sshFxpClosePacket{}
  124. case ssh_FXP_READ:
  125. pkt = &sshFxpReadPacket{}
  126. case ssh_FXP_WRITE:
  127. pkt = &sshFxpWritePacket{}
  128. readonly = false
  129. case ssh_FXP_FSTAT:
  130. pkt = &sshFxpFstatPacket{}
  131. case ssh_FXP_SETSTAT:
  132. pkt = &sshFxpSetstatPacket{}
  133. readonly = false
  134. case ssh_FXP_FSETSTAT:
  135. pkt = &sshFxpFsetstatPacket{}
  136. readonly = false
  137. case ssh_FXP_OPENDIR:
  138. pkt = &sshFxpOpendirPacket{}
  139. case ssh_FXP_READDIR:
  140. pkt = &sshFxpReaddirPacket{}
  141. case ssh_FXP_REMOVE:
  142. pkt = &sshFxpRemovePacket{}
  143. readonly = false
  144. case ssh_FXP_MKDIR:
  145. pkt = &sshFxpMkdirPacket{}
  146. readonly = false
  147. case ssh_FXP_RMDIR:
  148. pkt = &sshFxpRmdirPacket{}
  149. readonly = false
  150. case ssh_FXP_REALPATH:
  151. pkt = &sshFxpRealpathPacket{}
  152. case ssh_FXP_STAT:
  153. pkt = &sshFxpStatPacket{}
  154. case ssh_FXP_RENAME:
  155. pkt = &sshFxpRenamePacket{}
  156. readonly = false
  157. case ssh_FXP_READLINK:
  158. pkt = &sshFxpReadlinkPacket{}
  159. case ssh_FXP_SYMLINK:
  160. pkt = &sshFxpSymlinkPacket{}
  161. readonly = false
  162. case ssh_FXP_EXTENDED:
  163. pkt = &sshFxpExtendedPacket{}
  164. default:
  165. return errors.Errorf("unhandled packet type: %s", p.pktType)
  166. }
  167. if err := pkt.UnmarshalBinary(p.pktBytes); err != nil {
  168. return err
  169. }
  170. // handle FXP_OPENDIR specially
  171. switch pkt := pkt.(type) {
  172. case *sshFxpOpenPacket:
  173. readonly = pkt.readonly()
  174. case *sshFxpExtendedPacket:
  175. readonly = pkt.SpecificPacket.readonly()
  176. }
  177. // If server is operating read-only and a write operation is requested,
  178. // return permission denied
  179. if !readonly && svr.readOnly {
  180. if err := svr.sendError(pkt, syscall.EPERM); err != nil {
  181. return errors.Wrap(err, "failed to send read only packet response")
  182. }
  183. continue
  184. }
  185. if err := handlePacket(svr, pkt); err != nil {
  186. return err
  187. }
  188. }
  189. return nil
  190. }
  191. func handlePacket(s *Server, p interface{}) error {
  192. switch p := p.(type) {
  193. case *sshFxInitPacket:
  194. return s.sendPacket(sshFxVersionPacket{sftpProtocolVersion, nil})
  195. case *sshFxpStatPacket:
  196. // stat the requested file
  197. info, err := os.Stat(p.Path)
  198. if err != nil {
  199. return s.sendError(p, err)
  200. }
  201. return s.sendPacket(sshFxpStatResponse{
  202. ID: p.ID,
  203. info: info,
  204. })
  205. case *sshFxpLstatPacket:
  206. // stat the requested file
  207. info, err := os.Lstat(p.Path)
  208. if err != nil {
  209. return s.sendError(p, err)
  210. }
  211. return s.sendPacket(sshFxpStatResponse{
  212. ID: p.ID,
  213. info: info,
  214. })
  215. case *sshFxpFstatPacket:
  216. f, ok := s.getHandle(p.Handle)
  217. if !ok {
  218. return s.sendError(p, syscall.EBADF)
  219. }
  220. info, err := f.Stat()
  221. if err != nil {
  222. return s.sendError(p, err)
  223. }
  224. return s.sendPacket(sshFxpStatResponse{
  225. ID: p.ID,
  226. info: info,
  227. })
  228. case *sshFxpMkdirPacket:
  229. // TODO FIXME: ignore flags field
  230. err := os.Mkdir(p.Path, 0755)
  231. return s.sendError(p, err)
  232. case *sshFxpRmdirPacket:
  233. err := os.Remove(p.Path)
  234. return s.sendError(p, err)
  235. case *sshFxpRemovePacket:
  236. err := os.Remove(p.Filename)
  237. return s.sendError(p, err)
  238. case *sshFxpRenamePacket:
  239. err := os.Rename(p.Oldpath, p.Newpath)
  240. return s.sendError(p, err)
  241. case *sshFxpSymlinkPacket:
  242. err := os.Symlink(p.Targetpath, p.Linkpath)
  243. return s.sendError(p, err)
  244. case *sshFxpClosePacket:
  245. return s.sendError(p, s.closeHandle(p.Handle))
  246. case *sshFxpReadlinkPacket:
  247. f, err := os.Readlink(p.Path)
  248. if err != nil {
  249. return s.sendError(p, err)
  250. }
  251. return s.sendPacket(sshFxpNamePacket{
  252. ID: p.ID,
  253. NameAttrs: []sshFxpNameAttr{{
  254. Name: f,
  255. LongName: f,
  256. Attrs: emptyFileStat,
  257. }},
  258. })
  259. case *sshFxpRealpathPacket:
  260. f, err := filepath.Abs(p.Path)
  261. if err != nil {
  262. return s.sendError(p, err)
  263. }
  264. f = filepath.Clean(f)
  265. return s.sendPacket(sshFxpNamePacket{
  266. ID: p.ID,
  267. NameAttrs: []sshFxpNameAttr{{
  268. Name: f,
  269. LongName: f,
  270. Attrs: emptyFileStat,
  271. }},
  272. })
  273. case *sshFxpOpendirPacket:
  274. return sshFxpOpenPacket{
  275. ID: p.ID,
  276. Path: p.Path,
  277. Pflags: ssh_FXF_READ,
  278. }.respond(s)
  279. case *sshFxpReadPacket:
  280. f, ok := s.getHandle(p.Handle)
  281. if !ok {
  282. return s.sendError(p, syscall.EBADF)
  283. }
  284. data := make([]byte, clamp(p.Len, s.maxTxPacket))
  285. n, err := f.ReadAt(data, int64(p.Offset))
  286. if err != nil && (err != io.EOF || n == 0) {
  287. return s.sendError(p, err)
  288. }
  289. return s.sendPacket(sshFxpDataPacket{
  290. ID: p.ID,
  291. Length: uint32(n),
  292. Data: data[:n],
  293. })
  294. case *sshFxpWritePacket:
  295. f, ok := s.getHandle(p.Handle)
  296. if !ok {
  297. return s.sendError(p, syscall.EBADF)
  298. }
  299. _, err := f.WriteAt(p.Data, int64(p.Offset))
  300. return s.sendError(p, err)
  301. case serverRespondablePacket:
  302. err := p.respond(s)
  303. return errors.Wrap(err, "pkt.respond failed")
  304. default:
  305. return errors.Errorf("unexpected packet type %T", p)
  306. }
  307. }
  308. // Serve serves SFTP connections until the streams stop or the SFTP subsystem
  309. // is stopped.
  310. func (svr *Server) Serve() error {
  311. var wg sync.WaitGroup
  312. wg.Add(sftpServerWorkerCount)
  313. for i := 0; i < sftpServerWorkerCount; i++ {
  314. go func() {
  315. defer wg.Done()
  316. if err := svr.sftpServerWorker(); err != nil {
  317. svr.conn.Close() // shuts down recvPacket
  318. }
  319. }()
  320. }
  321. var err error
  322. var pktType uint8
  323. var pktBytes []byte
  324. for {
  325. pktType, pktBytes, err = svr.recvPacket()
  326. if err != nil {
  327. break
  328. }
  329. svr.pktChan <- rxPacket{fxp(pktType), pktBytes}
  330. }
  331. close(svr.pktChan) // shuts down sftpServerWorkers
  332. wg.Wait() // wait for all workers to exit
  333. // close any still-open files
  334. for handle, file := range svr.openFiles {
  335. fmt.Fprintf(svr.debugStream, "sftp server file with handle %q left open: %v\n", handle, file.Name())
  336. file.Close()
  337. }
  338. return err // error from recvPacket
  339. }
  340. type id interface {
  341. id() uint32
  342. }
  343. // The init packet has no ID, so we just return a zero-value ID
  344. func (p sshFxInitPacket) id() uint32 { return 0 }
  345. type sshFxpStatResponse struct {
  346. ID uint32
  347. info os.FileInfo
  348. }
  349. func (p sshFxpStatResponse) MarshalBinary() ([]byte, error) {
  350. b := []byte{ssh_FXP_ATTRS}
  351. b = marshalUint32(b, p.ID)
  352. b = marshalFileInfo(b, p.info)
  353. return b, nil
  354. }
  355. var emptyFileStat = []interface{}{uint32(0)}
  356. func (p sshFxpOpenPacket) readonly() bool {
  357. return !p.hasPflags(ssh_FXF_WRITE)
  358. }
  359. func (p sshFxpOpenPacket) hasPflags(flags ...uint32) bool {
  360. for _, f := range flags {
  361. if p.Pflags&f == 0 {
  362. return false
  363. }
  364. }
  365. return true
  366. }
  367. func (p sshFxpOpenPacket) respond(svr *Server) error {
  368. var osFlags int
  369. if p.hasPflags(ssh_FXF_READ, ssh_FXF_WRITE) {
  370. osFlags |= os.O_RDWR
  371. } else if p.hasPflags(ssh_FXF_WRITE) {
  372. osFlags |= os.O_WRONLY
  373. } else if p.hasPflags(ssh_FXF_READ) {
  374. osFlags |= os.O_RDONLY
  375. } else {
  376. // how are they opening?
  377. return svr.sendError(p, syscall.EINVAL)
  378. }
  379. if p.hasPflags(ssh_FXF_APPEND) {
  380. osFlags |= os.O_APPEND
  381. }
  382. if p.hasPflags(ssh_FXF_CREAT) {
  383. osFlags |= os.O_CREATE
  384. }
  385. if p.hasPflags(ssh_FXF_TRUNC) {
  386. osFlags |= os.O_TRUNC
  387. }
  388. if p.hasPflags(ssh_FXF_EXCL) {
  389. osFlags |= os.O_EXCL
  390. }
  391. f, err := os.OpenFile(p.Path, osFlags, 0644)
  392. if err != nil {
  393. return svr.sendError(p, err)
  394. }
  395. handle := svr.nextHandle(f)
  396. return svr.sendPacket(sshFxpHandlePacket{p.ID, handle})
  397. }
  398. func (p sshFxpReaddirPacket) respond(svr *Server) error {
  399. f, ok := svr.getHandle(p.Handle)
  400. if !ok {
  401. return svr.sendError(p, syscall.EBADF)
  402. }
  403. dirname := f.Name()
  404. dirents, err := f.Readdir(128)
  405. if err != nil {
  406. return svr.sendError(p, err)
  407. }
  408. ret := sshFxpNamePacket{ID: p.ID}
  409. for _, dirent := range dirents {
  410. ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{
  411. Name: dirent.Name(),
  412. LongName: runLs(dirname, dirent),
  413. Attrs: []interface{}{dirent},
  414. })
  415. }
  416. return svr.sendPacket(ret)
  417. }
  418. func (p sshFxpSetstatPacket) respond(svr *Server) error {
  419. // additional unmarshalling is required for each possibility here
  420. b := p.Attrs.([]byte)
  421. var err error
  422. debug("setstat name \"%s\"", p.Path)
  423. if (p.Flags & ssh_FILEXFER_ATTR_SIZE) != 0 {
  424. var size uint64
  425. if size, b, err = unmarshalUint64Safe(b); err == nil {
  426. err = os.Truncate(p.Path, int64(size))
  427. }
  428. }
  429. if (p.Flags & ssh_FILEXFER_ATTR_PERMISSIONS) != 0 {
  430. var mode uint32
  431. if mode, b, err = unmarshalUint32Safe(b); err == nil {
  432. err = os.Chmod(p.Path, os.FileMode(mode))
  433. }
  434. }
  435. if (p.Flags & ssh_FILEXFER_ATTR_ACMODTIME) != 0 {
  436. var atime uint32
  437. var mtime uint32
  438. if atime, b, err = unmarshalUint32Safe(b); err != nil {
  439. } else if mtime, b, err = unmarshalUint32Safe(b); err != nil {
  440. } else {
  441. atimeT := time.Unix(int64(atime), 0)
  442. mtimeT := time.Unix(int64(mtime), 0)
  443. err = os.Chtimes(p.Path, atimeT, mtimeT)
  444. }
  445. }
  446. if (p.Flags & ssh_FILEXFER_ATTR_UIDGID) != 0 {
  447. var uid uint32
  448. var gid uint32
  449. if uid, b, err = unmarshalUint32Safe(b); err != nil {
  450. } else if gid, b, err = unmarshalUint32Safe(b); err != nil {
  451. } else {
  452. err = os.Chown(p.Path, int(uid), int(gid))
  453. }
  454. }
  455. return svr.sendError(p, err)
  456. }
  457. func (p sshFxpFsetstatPacket) respond(svr *Server) error {
  458. f, ok := svr.getHandle(p.Handle)
  459. if !ok {
  460. return svr.sendError(p, syscall.EBADF)
  461. }
  462. // additional unmarshalling is required for each possibility here
  463. b := p.Attrs.([]byte)
  464. var err error
  465. debug("fsetstat name \"%s\"", f.Name())
  466. if (p.Flags & ssh_FILEXFER_ATTR_SIZE) != 0 {
  467. var size uint64
  468. if size, b, err = unmarshalUint64Safe(b); err == nil {
  469. err = f.Truncate(int64(size))
  470. }
  471. }
  472. if (p.Flags & ssh_FILEXFER_ATTR_PERMISSIONS) != 0 {
  473. var mode uint32
  474. if mode, b, err = unmarshalUint32Safe(b); err == nil {
  475. err = f.Chmod(os.FileMode(mode))
  476. }
  477. }
  478. if (p.Flags & ssh_FILEXFER_ATTR_ACMODTIME) != 0 {
  479. var atime uint32
  480. var mtime uint32
  481. if atime, b, err = unmarshalUint32Safe(b); err != nil {
  482. } else if mtime, b, err = unmarshalUint32Safe(b); err != nil {
  483. } else {
  484. atimeT := time.Unix(int64(atime), 0)
  485. mtimeT := time.Unix(int64(mtime), 0)
  486. err = os.Chtimes(f.Name(), atimeT, mtimeT)
  487. }
  488. }
  489. if (p.Flags & ssh_FILEXFER_ATTR_UIDGID) != 0 {
  490. var uid uint32
  491. var gid uint32
  492. if uid, b, err = unmarshalUint32Safe(b); err != nil {
  493. } else if gid, b, err = unmarshalUint32Safe(b); err != nil {
  494. } else {
  495. err = f.Chown(int(uid), int(gid))
  496. }
  497. }
  498. return svr.sendError(p, err)
  499. }
  500. // translateErrno translates a syscall error number to a SFTP error code.
  501. func translateErrno(errno syscall.Errno) uint32 {
  502. switch errno {
  503. case 0:
  504. return ssh_FX_OK
  505. case syscall.ENOENT:
  506. return ssh_FX_NO_SUCH_FILE
  507. case syscall.EPERM:
  508. return ssh_FX_PERMISSION_DENIED
  509. }
  510. return ssh_FX_FAILURE
  511. }
  512. func statusFromError(p id, err error) sshFxpStatusPacket {
  513. ret := sshFxpStatusPacket{
  514. ID: p.id(),
  515. StatusError: StatusError{
  516. // ssh_FX_OK = 0
  517. // ssh_FX_EOF = 1
  518. // ssh_FX_NO_SUCH_FILE = 2 ENOENT
  519. // ssh_FX_PERMISSION_DENIED = 3
  520. // ssh_FX_FAILURE = 4
  521. // ssh_FX_BAD_MESSAGE = 5
  522. // ssh_FX_NO_CONNECTION = 6
  523. // ssh_FX_CONNECTION_LOST = 7
  524. // ssh_FX_OP_UNSUPPORTED = 8
  525. Code: ssh_FX_OK,
  526. },
  527. }
  528. if err != nil {
  529. debug("statusFromError: error is %T %#v", err, err)
  530. ret.StatusError.Code = ssh_FX_FAILURE
  531. ret.StatusError.msg = err.Error()
  532. if err == io.EOF {
  533. ret.StatusError.Code = ssh_FX_EOF
  534. } else if errno, ok := err.(syscall.Errno); ok {
  535. ret.StatusError.Code = translateErrno(errno)
  536. } else if pathError, ok := err.(*os.PathError); ok {
  537. debug("statusFromError: error is %T %#v", pathError.Err, pathError.Err)
  538. if errno, ok := pathError.Err.(syscall.Errno); ok {
  539. ret.StatusError.Code = translateErrno(errno)
  540. }
  541. }
  542. }
  543. return ret
  544. }
  545. func clamp(v, max uint32) uint32 {
  546. if v > max {
  547. return max
  548. }
  549. return v
  550. }