From 592606a00d2fa444c168dfe0f027998dd2576c91 Mon Sep 17 00:00:00 2001 From: Laurynas Gadliauskas Date: Wed, 9 Jun 2021 10:37:29 +0300 Subject: [PATCH] fix: check path for OoS symlinks --- files/file.go | 17 ----------------- http/resource.go | 44 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/files/file.go b/files/file.go index bfec691f..51678295 100644 --- a/files/file.go +++ b/files/file.go @@ -65,21 +65,6 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) { return nil, os.ErrPermission } - isSymlink := false - symlink := "" - if lst, ok := opts.Fs.(afero.Lstater); ok { - info, _, err := lst.LstatIfPossible(opts.Path) - if err == nil { - isSymlink = IsSymlink(info.Mode()) - } - } - - if isSymlink { - if lsf, ok := opts.Fs.(afero.LinkReader); ok { - symlink, _ = lsf.ReadlinkIfPossible(opts.Path) - } - } - info, err := opts.Fs.Stat(opts.Path) if err != nil { return nil, err @@ -92,8 +77,6 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) { ModTime: info.ModTime(), Mode: info.Mode(), IsDir: info.IsDir(), - IsSymlink: isSymlink, - Link: symlink, Size: info.Size(), Extension: filepath.Ext(info.Name()), Token: opts.Token, diff --git a/http/resource.go b/http/resource.go index 644c624d..e951abb4 100644 --- a/http/resource.go +++ b/http/resource.go @@ -37,15 +37,9 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d return errToStatus(err), err } - // check if symlink's target is within base path - if file.IsSymlink { - parentDir := filepath.Dir(filepath.Clean(file.Path)) - fullLinkTarget := filepath.Join(d.user.FullPath(parentDir), file.Link) - scopedLinkTarget := d.user.FullPath(filepath.Join(parentDir, file.Link)) - if fullLinkTarget != scopedLinkTarget { - err = errors.ErrNotExist - return errToStatus(err), err - } + err = checkOutOfScopeSymlink(d, file.Path) + if err != nil { + return errToStatus(err), err } if r.URL.Query().Get("disk_usage") == "true" { @@ -322,6 +316,38 @@ func checkParent(src, dst string) error { return nil } +// Checks if path contains symlink to out-of-scope targets. +// Returns error ErrNotExist if it does. +func checkOutOfScopeSymlink(d *data, path string) error { + lsf, ok := d.user.Fs.(afero.LinkReader) + if !ok { + return nil + } + + var parts []string + for _, part := range strings.Split(path, string(os.PathSeparator)) { + if part != "" { + parts = append(parts, part) + } + } + + evalPath := string(os.PathSeparator) + for _, part := range parts { + evalPath = filepath.Join(evalPath, filepath.Clean(part)) + symlink, err := lsf.ReadlinkIfPossible(evalPath) + if err == nil { + parentDir := filepath.Dir(evalPath) + fullLinkTarget := filepath.Join(d.user.FullPath(parentDir), symlink) + scopedLinkTarget := d.user.FullPath(filepath.Join(parentDir, symlink)) + if fullLinkTarget != scopedLinkTarget { + return errors.ErrNotExist + } + } + } + + return nil +} + func addVersionSuffix(source string, fs afero.Fs) string { counter := 1 dir, name := path.Split(source)