golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/elevate/doas.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package elevate 7 8 import ( 9 "errors" 10 "os" 11 "runtime" 12 "strings" 13 "unsafe" 14 15 "golang.org/x/sys/windows" 16 "golang.org/x/sys/windows/svc/mgr" 17 ) 18 19 func setAllEnv(env []string) { 20 windows.Clearenv() 21 for _, e := range env { 22 k, v, ok := strings.Cut(e, "=") 23 if !ok { 24 continue 25 } 26 windows.Setenv(k, v) 27 } 28 } 29 30 func DoAsSystem(f func() error) error { 31 runtime.LockOSThread() 32 defer func() { 33 windows.RevertToSelf() 34 runtime.UnlockOSThread() 35 }() 36 privileges := windows.Tokenprivileges{ 37 PrivilegeCount: 1, 38 Privileges: [1]windows.LUIDAndAttributes{ 39 { 40 Attributes: windows.SE_PRIVILEGE_ENABLED, 41 }, 42 }, 43 } 44 err := windows.LookupPrivilegeValue(nil, windows.StringToUTF16Ptr("SeDebugPrivilege"), &privileges.Privileges[0].Luid) 45 if err != nil { 46 return err 47 } 48 err = windows.ImpersonateSelf(windows.SecurityImpersonation) 49 if err != nil { 50 return err 51 } 52 var threadToken windows.Token 53 err = windows.OpenThreadToken(windows.CurrentThread(), windows.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, false, &threadToken) 54 if err != nil { 55 return err 56 } 57 tokenUser, err := threadToken.GetTokenUser() 58 if err == nil && tokenUser.User.Sid.IsWellKnown(windows.WinLocalSystemSid) { 59 threadToken.Close() 60 return f() 61 } 62 err = windows.AdjustTokenPrivileges(threadToken, false, &privileges, uint32(unsafe.Sizeof(privileges)), nil, nil) 63 threadToken.Close() 64 if err != nil { 65 return err 66 } 67 68 processes, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0) 69 if err != nil { 70 return err 71 } 72 processEntry := windows.ProcessEntry32{Size: uint32(unsafe.Sizeof(windows.ProcessEntry32{}))} 73 var impersonationError error 74 for err = windows.Process32First(processes, &processEntry); err == nil; err = windows.Process32Next(processes, &processEntry) { 75 if strings.ToLower(windows.UTF16ToString(processEntry.ExeFile[:])) != "winlogon.exe" { 76 continue 77 } 78 winlogonProcess, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, processEntry.ProcessID) 79 if err != nil { 80 impersonationError = err 81 continue 82 } 83 var winlogonToken windows.Token 84 err = windows.OpenProcessToken(winlogonProcess, windows.TOKEN_QUERY|windows.TOKEN_IMPERSONATE|windows.TOKEN_DUPLICATE, &winlogonToken) 85 windows.CloseHandle(winlogonProcess) 86 if err != nil { 87 continue 88 } 89 tokenUser, err := winlogonToken.GetTokenUser() 90 if err != nil || !tokenUser.User.Sid.IsWellKnown(windows.WinLocalSystemSid) { 91 winlogonToken.Close() 92 continue 93 } 94 windows.CloseHandle(processes) 95 96 var duplicatedToken windows.Token 97 err = windows.DuplicateTokenEx(winlogonToken, 0, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &duplicatedToken) 98 windows.CloseHandle(winlogonProcess) 99 if err != nil { 100 return err 101 } 102 newEnv, err := duplicatedToken.Environ(false) 103 if err != nil { 104 duplicatedToken.Close() 105 return err 106 } 107 currentEnv := os.Environ() 108 err = windows.SetThreadToken(nil, duplicatedToken) 109 duplicatedToken.Close() 110 if err != nil { 111 return err 112 } 113 setAllEnv(newEnv) 114 err = f() 115 setAllEnv(currentEnv) 116 return err 117 } 118 windows.CloseHandle(processes) 119 if impersonationError != nil { 120 return impersonationError 121 } 122 return errors.New("unable to find winlogon.exe process") 123 } 124 125 func DoAsService(serviceName string, f func() error) error { 126 scm, err := mgr.Connect() 127 if err != nil { 128 return err 129 } 130 service, err := scm.OpenService(serviceName) 131 scm.Disconnect() 132 if err != nil { 133 return err 134 } 135 status, err := service.Query() 136 service.Close() 137 if err != nil { 138 return err 139 } 140 if status.ProcessId == 0 { 141 return errors.New("service is not running") 142 } 143 return DoAsSystem(func() error { 144 serviceProcess, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, status.ProcessId) 145 if err != nil { 146 return err 147 } 148 var serviceToken windows.Token 149 err = windows.OpenProcessToken(serviceProcess, windows.TOKEN_IMPERSONATE|windows.TOKEN_DUPLICATE, &serviceToken) 150 windows.CloseHandle(serviceProcess) 151 if err != nil { 152 return err 153 } 154 var duplicatedToken windows.Token 155 err = windows.DuplicateTokenEx(serviceToken, 0, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &duplicatedToken) 156 serviceToken.Close() 157 if err != nil { 158 return err 159 } 160 newEnv, err := duplicatedToken.Environ(false) 161 if err != nil { 162 duplicatedToken.Close() 163 return err 164 } 165 currentEnv := os.Environ() 166 err = windows.SetThreadToken(nil, duplicatedToken) 167 duplicatedToken.Close() 168 if err != nil { 169 return err 170 } 171 setAllEnv(newEnv) 172 err = f() 173 setAllEnv(currentEnv) 174 return err 175 }) 176 }