2
0

clientupdate_windows.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Windows-specific stuff that can't go in clientupdate.go because it needs
  4. // x/sys/windows.
  5. package clientupdate
  6. import (
  7. "errors"
  8. "fmt"
  9. "os/exec"
  10. "os/user"
  11. "path/filepath"
  12. "syscall"
  13. "unsafe"
  14. "golang.org/x/sys/windows"
  15. "tailscale.com/util/winutil/authenticode"
  16. )
  17. func init() {
  18. markTempFileFunc = markTempFileWindows
  19. verifyAuthenticode = verifyTailscale
  20. launchTailscaleAsWinGUIUser = launchTailscaleAsGUIUser
  21. }
  22. func markTempFileWindows(name string) error {
  23. name16 := windows.StringToUTF16Ptr(name)
  24. return windows.MoveFileEx(name16, nil, windows.MOVEFILE_DELAY_UNTIL_REBOOT)
  25. }
  26. const certSubjectTailscale = "Tailscale Inc."
  27. func verifyTailscale(path string) error {
  28. return authenticode.Verify(path, certSubjectTailscale)
  29. }
  30. func launchTailscaleAsGUIUser(exePath string) error {
  31. exePath = filepath.Join(filepath.Dir(exePath), "tailscale-ipn.exe")
  32. var token windows.Token
  33. if u, err := user.Current(); err == nil && u.Name == "SYSTEM" {
  34. sessionID, err := wtsGetActiveSessionID()
  35. if err != nil {
  36. return fmt.Errorf("wtsGetActiveSessionID(): %w", err)
  37. }
  38. if err := windows.WTSQueryUserToken(sessionID, &token); err != nil {
  39. return fmt.Errorf("WTSQueryUserToken (0x%x): %w", sessionID, err)
  40. }
  41. defer token.Close()
  42. }
  43. cmd := exec.Command(exePath)
  44. cmd.SysProcAttr = &syscall.SysProcAttr{
  45. Token: syscall.Token(token),
  46. HideWindow: true,
  47. }
  48. return cmd.Start()
  49. }
  50. func wtsGetActiveSessionID() (uint32, error) {
  51. var (
  52. sessionInfo *windows.WTS_SESSION_INFO
  53. count uint32 = 0
  54. )
  55. const WTS_CURRENT_SERVER_HANDLE = 0
  56. if err := windows.WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessionInfo, &count); err != nil {
  57. return 0, fmt.Errorf("WTSEnumerateSessions: %w", err)
  58. }
  59. defer windows.WTSFreeMemory(uintptr(unsafe.Pointer(sessionInfo)))
  60. current := unsafe.Pointer(sessionInfo)
  61. for i := uint32(0); i < count; i++ {
  62. session := (*windows.WTS_SESSION_INFO)(current)
  63. if session.State == windows.WTSActive {
  64. return session.SessionID, nil
  65. }
  66. current = unsafe.Add(current, unsafe.Sizeof(windows.WTS_SESSION_INFO{}))
  67. }
  68. return 0, errors.New("no active desktop sessions found")
  69. }