package rootstate import ( "os" "path/filepath" "strings" "github.com/carlosframework/platform/internal/safefile" "golang.org/x/sys/unix" ) func OpenAuthorityFile(path string) (*os.File, error) { if !filepath.IsAbs(path) || filepath.Clean(path) != path || path == "/" { return nil, os.ErrPermission } parent, err := os.Open("/") if err != nil { return nil, err } defer parent.Close() return openAuthorityFile(parent, strings.Split(strings.TrimPrefix(path, "/"), "/"), 0) } func openAuthorityFile(parent *os.File, parts []string, owner uint32) (*os.File, error) { if len(parts) == 0 { return nil, os.ErrPermission } fd, err := unix.FcntlInt(parent.Fd(), unix.F_DUPFD_CLOEXEC, 0) if err != nil { return nil, err } defer func() { unix.Close(fd) }() if err := trusted(fd, owner, unix.S_IFDIR); err != nil { return nil, err } for i, name := range parts { if !component(name) { return nil, os.ErrPermission } if i == len(parts)-1 { break } next, err := unix.Openat(fd, name, unix.O_RDONLY|unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) if err != nil { return nil, err } unix.Close(fd) fd = next if err := trusted(fd, owner, unix.S_IFDIR); err != nil { return nil, err } } f, err := safefile.OpenFileAt(fd, parts[len(parts)-1], os.O_RDONLY, 0) if err != nil { return nil, err } if err := trusted(int(f.Fd()), owner, unix.S_IFREG); err != nil { f.Close() return nil, err } return f, nil }