Files
sing-box/experimental/boxdd/insecure_mode_supported.go
2026-07-20 22:00:35 +08:00

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)
}