178 lines
4.3 KiB
Go
178 lines
4.3 KiB
Go
//go:build windows || linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/sagernet/sing-box/experimental/locale"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
)
|
|
|
|
func insecureFeatureError(ctx context.Context, feature string) error {
|
|
return E.New(fmt.Sprintf(locale.FromContext(ctx).InsecureFeatureMessage, feature, insecureModePlatformName()))
|
|
}
|
|
|
|
type daemonSecurityPolicy struct {
|
|
daemon *Daemon
|
|
}
|
|
|
|
func (p *daemonSecurityPolicy) CheckFeature(ctx context.Context, feature string) error {
|
|
if p.daemon.insecureModeEnabled() {
|
|
return nil
|
|
}
|
|
return insecureFeatureError(ctx, feature)
|
|
}
|
|
|
|
type restrictedFileManager struct {
|
|
daemon *Daemon
|
|
}
|
|
|
|
func (m *restrictedFileManager) BasePath(name string) string {
|
|
if filepath.IsAbs(name) {
|
|
return name
|
|
}
|
|
currentDirectory, err := os.Getwd()
|
|
if err != nil {
|
|
return name
|
|
}
|
|
return filepath.Join(currentDirectory, name)
|
|
}
|
|
|
|
func (m *restrictedFileManager) TempPath() string {
|
|
currentDirectory, err := os.Getwd()
|
|
if err != nil {
|
|
return "."
|
|
}
|
|
return currentDirectory
|
|
}
|
|
|
|
func (m *restrictedFileManager) checkPath(name string) (string, error) {
|
|
path, err := filepath.Abs(m.BasePath(name))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if m.daemon.insecureModeEnabled() {
|
|
return path, nil
|
|
}
|
|
currentDirectory, err := os.Getwd()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
normalizedRoot := normalizeRestrictedPath(currentDirectory)
|
|
normalizedPath := normalizeRestrictedPath(path)
|
|
if normalizedPath != normalizedRoot && !strings.HasPrefix(normalizedPath, normalizedRoot+string(filepath.Separator)) {
|
|
return "", E.New(fmt.Sprintf(locale.Current().ExternalPathFeature, path, insecureModePlatformName()))
|
|
}
|
|
existingPath := path
|
|
for {
|
|
_, err = os.Lstat(existingPath)
|
|
if err == nil {
|
|
break
|
|
}
|
|
if !os.IsNotExist(err) {
|
|
return "", err
|
|
}
|
|
parentPath := filepath.Dir(existingPath)
|
|
if parentPath == existingPath {
|
|
return "", err
|
|
}
|
|
existingPath = parentPath
|
|
}
|
|
resolvedRoot, err := filepath.EvalSymlinks(currentDirectory)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
resolvedExistingPath, err := filepath.EvalSymlinks(existingPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
remainingPath, err := filepath.Rel(existingPath, path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
resolvedPath := filepath.Join(resolvedExistingPath, remainingPath)
|
|
normalizedResolvedRoot := normalizeRestrictedPath(resolvedRoot)
|
|
normalizedResolvedPath := normalizeRestrictedPath(resolvedPath)
|
|
if normalizedResolvedPath != normalizedResolvedRoot && !strings.HasPrefix(normalizedResolvedPath, normalizedResolvedRoot+string(filepath.Separator)) {
|
|
return "", E.New(fmt.Sprintf(locale.Current().ExternalPathFeature, path, insecureModePlatformName()))
|
|
}
|
|
return path, nil
|
|
}
|
|
|
|
func (m *restrictedFileManager) OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) {
|
|
path, err := m.checkPath(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return os.OpenFile(path, flag, perm)
|
|
}
|
|
|
|
func (m *restrictedFileManager) Create(name string) (*os.File, error) {
|
|
path, err := m.checkPath(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return os.Create(path)
|
|
}
|
|
|
|
func (m *restrictedFileManager) CreateTemp(pattern string) (*os.File, error) {
|
|
currentDirectory, err := os.Getwd()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return os.CreateTemp(currentDirectory, pattern)
|
|
}
|
|
|
|
func (m *restrictedFileManager) Chown(path string) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *restrictedFileManager) Mkdir(path string, perm os.FileMode) error {
|
|
checkedPath, err := m.checkPath(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.Mkdir(checkedPath, perm)
|
|
}
|
|
|
|
func (m *restrictedFileManager) MkdirAll(path string, perm os.FileMode) error {
|
|
checkedPath, err := m.checkPath(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.MkdirAll(checkedPath, perm)
|
|
}
|
|
|
|
func (m *restrictedFileManager) Remove(path string) error {
|
|
checkedPath, err := m.checkPath(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.Remove(checkedPath)
|
|
}
|
|
|
|
func (m *restrictedFileManager) RemoveAll(path string) error {
|
|
checkedPath, err := m.checkPath(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.RemoveAll(checkedPath)
|
|
}
|
|
|
|
func (m *restrictedFileManager) Rename(oldPath string, newPath string) error {
|
|
checkedOldPath, err := m.checkPath(oldPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
checkedNewPath, err := m.checkPath(newPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.Rename(checkedOldPath, checkedNewPath)
|
|
}
|