aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/manager/service.go
diff options
context:
space:
mode:
Diffstat (limited to 'manager/service.go')
-rw-r--r--manager/service.go157
1 files changed, 81 insertions, 76 deletions
diff --git a/manager/service.go b/manager/service.go
index 6c3b039b..47e20d45 100644
--- a/manager/service.go
+++ b/manager/service.go
@@ -1,46 +1,32 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
*/
package manager
import (
"errors"
- "fmt"
"log"
"os"
"runtime"
- "runtime/debug"
- "strings"
+ "strconv"
"sync"
- "syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
+ "golang.zx2c4.com/wireguard/windows/driver"
"golang.zx2c4.com/wireguard/windows/conf"
"golang.zx2c4.com/wireguard/windows/elevate"
"golang.zx2c4.com/wireguard/windows/ringlogger"
"golang.zx2c4.com/wireguard/windows/services"
- "golang.zx2c4.com/wireguard/windows/version"
)
type managerService struct{}
-func printPanic() {
- if x := recover(); x != nil {
- for _, line := range append([]string{fmt.Sprint(x)}, strings.Split(string(debug.Stack()), "\n")...) {
- if len(strings.TrimSpace(line)) > 0 {
- log.Println(line)
- }
- }
- panic(x)
- }
-}
-
func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
changes <- svc.Status{State: svc.StartPending}
@@ -56,40 +42,40 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
changes <- svc.Status{State: svc.StopPending}
}()
- err = ringlogger.InitGlobalLogger("MGR")
+ var logFile string
+ logFile, err = conf.LogFile(true)
if err != nil {
serviceError = services.ErrorRingloggerOpen
return
}
- defer printPanic()
-
- log.Println("Starting", version.UserAgent())
-
- path, err := os.Executable()
+ err = ringlogger.InitGlobalLogger(logFile, "MGR")
if err != nil {
- serviceError = services.ErrorDetermineExecutablePath
+ serviceError = services.ErrorRingloggerOpen
return
}
- devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, 0)
+ services.PrintStarting()
+
+ path, err := os.Executable()
if err != nil {
- serviceError = services.ErrorOpenNULFile
+ serviceError = services.ErrorDetermineExecutablePath
return
}
- err = trackExistingTunnels()
+ err = watchNewTunnelServices()
if err != nil {
serviceError = services.ErrorTrackTunnels
return
}
- conf.RegisterStoreChangeCallback(func() { conf.MigrateUnencryptedConfigs() }) // Ignore return value for now, but could be useful later.
+ conf.RegisterStoreChangeCallback(func() { conf.MigrateUnencryptedConfigs(changeTunnelServiceConfigFilePath) })
conf.RegisterStoreChangeCallback(IPCServerNotifyTunnelsChange)
- procs := make(map[uint32]*os.Process)
+ procs := make(map[uint32]*uiProcess)
aliveSessions := make(map[uint32]bool)
procsLock := sync.Mutex{}
stoppingManager := false
+ operatorGroupSid, _ := windows.CreateWellKnownSid(windows.WinBuiltinNetworkConfigurationOperatorsSid)
startProcess := func(session uint32) {
defer func() {
@@ -104,7 +90,24 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
if err != nil {
return
}
- if !elevate.TokenIsElevatedOrElevatable(userToken) {
+ isAdmin := elevate.TokenIsElevatedOrElevatable(userToken)
+ isOperator := false
+ if !isAdmin && conf.AdminBool("LimitedOperatorUI") && operatorGroupSid != nil {
+ linkedToken, err := userToken.GetLinkedToken()
+ var impersonationToken windows.Token
+ if err == nil {
+ err = windows.DuplicateTokenEx(linkedToken, windows.TOKEN_QUERY, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &impersonationToken)
+ linkedToken.Close()
+ } else {
+ err = windows.DuplicateTokenEx(userToken, windows.TOKEN_QUERY, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &impersonationToken)
+ }
+ if err == nil {
+ isOperator, err = impersonationToken.IsMember(operatorGroupSid)
+ isOperator = isOperator && err == nil
+ impersonationToken.Close()
+ }
+ }
+ if !isAdmin && !isOperator {
userToken.Close()
return
}
@@ -125,23 +128,28 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
return
}
userProfileDirectory, _ := userToken.GetUserProfileDirectory()
- var elevatedToken windows.Token
- if userToken.IsElevated() {
- elevatedToken = userToken
- } else {
- elevatedToken, err = userToken.GetLinkedToken()
- userToken.Close()
- if err != nil {
- log.Printf("Unable to elevate token: %v", err)
- return
- }
- if !elevatedToken.IsElevated() {
- elevatedToken.Close()
- log.Println("Linked token is not elevated")
- return
+ var elevatedToken, runToken windows.Token
+ if isAdmin {
+ if userToken.IsElevated() {
+ elevatedToken = userToken
+ } else {
+ elevatedToken, err = userToken.GetLinkedToken()
+ userToken.Close()
+ if err != nil {
+ log.Printf("Unable to elevate token: %v", err)
+ return
+ }
+ if !elevatedToken.IsElevated() {
+ elevatedToken.Close()
+ log.Println("Linked token is not elevated")
+ return
+ }
}
+ runToken = elevatedToken
+ } else {
+ runToken = userToken
}
- defer elevatedToken.Close()
+ defer runToken.Close()
userToken = 0
first := true
for {
@@ -162,39 +170,45 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
first = false
}
- // TODO: we lock the OS thread so that these inheritable handles don't escape into other processes that
- // might be running in parallel Go routines. But the Go runtime is strange and who knows what's really
- // happening with these or what is inherited. We need to do some analysis to be certain of what's going on.
- runtime.LockOSThread()
- ourReader, theirReader, theirReaderStr, ourWriter, theirWriter, theirWriterStr, err := inheritableSocketpairEmulation()
+ ourReader, theirWriter, err := os.Pipe()
+ if err != nil {
+ log.Printf("Unable to create pipe: %v", err)
+ return
+ }
+ theirReader, ourWriter, err := os.Pipe()
if err != nil {
- log.Printf("Unable to create two inheritable RPC pipes: %v", err)
+ log.Printf("Unable to create pipe: %v", err)
return
}
- ourEvents, theirEvents, theirEventStr, err := inheritableEvents()
+ theirEvents, ourEvents, err := os.Pipe()
if err != nil {
- log.Printf("Unable to create one inheritable events pipe: %v", err)
+ log.Printf("Unable to create pipe: %v", err)
return
}
IPCServerListen(ourReader, ourWriter, ourEvents, elevatedToken)
- theirLogMapping, theirLogMappingHandle, err := ringlogger.Global.ExportInheritableMappingHandleStr()
+ theirLogMapping, err := ringlogger.Global.ExportInheritableMappingHandle()
if err != nil {
log.Printf("Unable to export inheritable mapping handle for logging: %v", err)
return
}
log.Printf("Starting UI process for user ā€˜%s@%sā€™ for session %d", username, domain, session)
- attr := &os.ProcAttr{
- Sys: &syscall.SysProcAttr{
- Token: syscall.Token(elevatedToken),
- },
- Files: []*os.File{devNull, devNull, devNull},
- Dir: userProfileDirectory,
- }
procsLock.Lock()
- var proc *os.Process
+ var proc *uiProcess
if alive := aliveSessions[session]; alive {
- proc, err = os.StartProcess(path, []string{path, "/ui", theirReaderStr, theirWriterStr, theirEventStr, theirLogMapping}, attr)
+ proc, err = launchUIProcess(path, []string{
+ path,
+ "/ui",
+ strconv.FormatUint(uint64(theirReader.Fd()), 10),
+ strconv.FormatUint(uint64(theirWriter.Fd()), 10),
+ strconv.FormatUint(uint64(theirEvents.Fd()), 10),
+ strconv.FormatUint(uint64(theirLogMapping), 10),
+ }, userProfileDirectory, []windows.Handle{
+ windows.Handle(theirReader.Fd()),
+ windows.Handle(theirWriter.Fd()),
+ windows.Handle(theirEvents.Fd()),
+ theirLogMapping,
+ }, runToken)
} else {
err = errors.New("Session has logged out")
}
@@ -202,8 +216,7 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
theirReader.Close()
theirWriter.Close()
theirEvents.Close()
- windows.Close(theirLogMappingHandle)
- runtime.UnlockOSThread()
+ windows.CloseHandle(theirLogMapping)
if err != nil {
ourReader.Close()
ourWriter.Close()
@@ -217,9 +230,7 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
procsLock.Unlock()
sessionIsDead := false
- processStatus, err := proc.Wait()
- if err == nil {
- exitCode := processStatus.Sys().(syscall.WaitStatus).ExitCode
+ if exitCode, err := proc.Wait(); err == nil {
log.Printf("Exited UI process for user '%s@%s' for session %d with status %x", username, domain, session, exitCode)
const STATUS_DLL_INIT_FAILED_LOGOFF = 0xC000026B
sessionIsDead = exitCode == STATUS_DLL_INIT_FAILED_LOGOFF
@@ -243,14 +254,13 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
goStartProcess := func(session uint32) {
procsGroup.Add(1)
go func() {
- defer printPanic()
startProcess(session)
procsGroup.Done()
}()
}
- time.AfterFunc(time.Second*10, cleanupStaleWintunInterfaces)
go checkForUpdates()
+ go driver.UninstallLegacyWintun() // We uninstall opportunistically here, so that we don't have to carry around the uninstaller code forever.
var sessionsPointer *windows.WTS_SESSION_INFO
var count uint32
@@ -259,12 +269,7 @@ func (service *managerService) Execute(args []string, r <-chan svc.ChangeRequest
serviceError = services.ErrorEnumerateSessions
return
}
- sessions := *(*[]windows.WTS_SESSION_INFO)(unsafe.Pointer(&struct {
- addr *windows.WTS_SESSION_INFO
- len int
- cap int
- }{sessionsPointer, int(count), int(count)}))
- for _, session := range sessions {
+ for _, session := range unsafe.Slice(sessionsPointer, count) {
if session.State != windows.WTSActive && session.State != windows.WTSDisconnected {
continue
}