From 48c5124500d66b35720d19bdaf3d5f78efdaa71f Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 17 Apr 2022 15:32:45 +0200 Subject: [PATCH] unpack tar files copied via sftp subsystem, too (not just older scp) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For compatibility with OpenSSH ≥ 9 --- scp.go | 64 ++++++++++++++++++++++++++++++++-------------------------- ssh.go | 29 ++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/scp.go b/scp.go index 9229c7c..9af6e8a 100644 --- a/scp.go +++ b/scp.go @@ -66,36 +66,9 @@ func scpSink(channel ssh.Channel, req *ssh.Request, cmdline []string) error { // Retrieve file contents var cw countingWriter - tr := tar.NewReader(io.TeeReader(channel, &cw)) - for { - h, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return err - } - - log.Printf("extracting %q", h.Name) - if err := os.MkdirAll(filepath.Dir(h.Name), 0700); err != nil { - return err - } - if strings.HasSuffix(h.Name, "/") { - continue // directory, don’t try to OpenFile() it - } - mode := h.FileInfo().Mode() & os.ModePerm - out, err := renameio.NewPendingFile(h.Name, renameio.WithStaticPermissions(mode)) - if err != nil { - return err - } - if _, err := io.Copy(out, tr); err != nil { - return err - } - if err := out.CloseAtomicallyReplace(); err != nil { - return err - } + if err := unpackTar(io.TeeReader(channel, &cw)); err != nil { + return err } - if rest := size - int64(cw); rest > 0 { buf := make([]byte, rest) if _, err := channel.Read(buf); err != nil { @@ -125,3 +98,36 @@ func scpSink(channel ssh.Channel, req *ssh.Request, cmdline []string) error { req.Reply(true, nil) return nil } + +func unpackTar(r io.Reader) error { + tr := tar.NewReader(r) + for { + h, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + log.Printf("extracting %q", h.Name) + if err := os.MkdirAll(filepath.Dir(h.Name), 0700); err != nil { + return err + } + if strings.HasSuffix(h.Name, "/") { + continue // directory, don’t try to OpenFile() it + } + mode := h.FileInfo().Mode() & os.ModePerm + out, err := renameio.NewPendingFile(h.Name, renameio.WithStaticPermissions(mode)) + if err != nil { + return err + } + if _, err := io.Copy(out, tr); err != nil { + return err + } + if err := out.CloseAtomicallyReplace(); err != nil { + return err + } + } + return nil +} diff --git a/ssh.go b/ssh.go index 551e12c..6913c97 100644 --- a/ssh.go +++ b/ssh.go @@ -292,9 +292,34 @@ func (s *session) request(ctx context.Context, req *ssh.Request) error { } } + // Special case for breakglass usage: unpack all .tar files that were + // transferred into $PWD (which is a /tmp/breakglass… temporary + // directory), so that the binaries included in the tar file can be used + // for debugging. + dirents, err := os.ReadDir(".") + if err != nil { + return err + } + for _, dirent := range dirents { + if !strings.HasSuffix(dirent.Name(), ".tar") { + continue + } + f, err := os.Open(dirent.Name()) + if err != nil { + return err + } + defer f.Close() + if err := unpackTar(f); err != nil { + return err + } + } + // See https://tools.ietf.org/html/rfc4254#section-6.10 - _, err = s.channel.SendRequest("exit-status", false /* wantReply */, ssh.Marshal(exitStatus{exitCode})) - return err + if _, err := s.channel.SendRequest("exit-status", false /* wantReply */, ssh.Marshal(exitStatus{exitCode})); err != nil { + return err + } + + return nil case "shell": req.Payload = []byte("\x00\x00\x00\x02sh")