mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-06-29 12:11:22 +00:00
98 lines
2.8 KiB
Go
98 lines
2.8 KiB
Go
//go:build with_gvisor && windows
|
|
|
|
package tailssh
|
|
|
|
import (
|
|
"os"
|
|
"os/user"
|
|
"strings"
|
|
|
|
gliderssh "github.com/sagernet/gliderssh"
|
|
"github.com/sagernet/sing-box/adapter"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
"github.com/sagernet/tailscale/util/winutil"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
func isPrivilegedUser() bool {
|
|
return winutil.IsCurrentProcessElevated()
|
|
}
|
|
|
|
// requestedUserMatchesProcess reports whether the ACL-mapped user is the same Windows
|
|
// account the sing-box process runs as. Windows has no impersonation wired up, so a
|
|
// session always runs with the process identity; this is the only case where the
|
|
// identity it runs as equals the requested one.
|
|
func requestedUserMatchesProcess(localUser *adapter.PlatformUser) (bool, error) {
|
|
tokenUser, err := windows.GetCurrentProcessToken().GetTokenUser()
|
|
if err != nil {
|
|
return false, E.Cause(err, "query process token user")
|
|
}
|
|
requested, err := user.Lookup(localUser.Username)
|
|
if err != nil {
|
|
return false, E.Cause(err, "lookup requested user")
|
|
}
|
|
// On Windows os/user reports SIDs in the Uid field.
|
|
return strings.EqualFold(tokenUser.User.Sid.String(), requested.Uid), nil
|
|
}
|
|
|
|
// verifyShellIdentity refuses a spawned shell/SFTP session whose ACL-mapped user differs
|
|
// from the process identity it would actually run as, since Windows has no impersonation.
|
|
func verifyShellIdentity(localUser *adapter.PlatformUser) error {
|
|
match, err := requestedUserMatchesProcess(localUser)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !match {
|
|
return E.New("Windows SSH sessions run as the sing-box process identity; mapping to a different local user (", localUser.Username, ") requires impersonation, which is not implemented")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func systemHostKeyPath() string {
|
|
return ""
|
|
}
|
|
|
|
func defaultPathEnv() string {
|
|
systemRoot := os.Getenv("SystemRoot")
|
|
return systemRoot + `\system32;` + systemRoot + `;` + systemRoot + `\System32\Wbem`
|
|
}
|
|
|
|
func userSocketDirectories(localUser *adapter.PlatformUser) []string {
|
|
return []string{localUser.HomeDir, os.TempDir()}
|
|
}
|
|
|
|
// prepareAgentSocket is a no-op on Windows: shells run as the server identity, so
|
|
// the agent socket needs no ownership change.
|
|
func prepareAgentSocket(_ string, _, _ int) error {
|
|
return nil
|
|
}
|
|
|
|
func platformEnvironment(localUser *adapter.PlatformUser) []string {
|
|
var env []string
|
|
env = append(env, "USERPROFILE="+localUser.HomeDir)
|
|
drive, path, found := strings.Cut(localUser.HomeDir, `\`)
|
|
if found && len(drive) == 2 && drive[1] == ':' {
|
|
env = append(env, "HOMEDRIVE="+drive)
|
|
env = append(env, `HOMEPATH=\`+path)
|
|
}
|
|
env = append(env, "SYSTEMROOT="+os.Getenv("SystemRoot"))
|
|
return env
|
|
}
|
|
|
|
func sftpCommand(sftpPath string) string {
|
|
return sftpPath
|
|
}
|
|
|
|
func sshSignalToSyscall(sig gliderssh.Signal) int {
|
|
switch sig {
|
|
case gliderssh.SIGINT:
|
|
return 2
|
|
case gliderssh.SIGTERM:
|
|
return 15
|
|
case gliderssh.SIGKILL:
|
|
return 9
|
|
default:
|
|
return 0
|
|
}
|
|
}
|