diff options
Diffstat (limited to 'manager/service.go')
-rw-r--r-- | manager/service.go | 157 |
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 } |