Closed mdlayher closed 5 years ago
This CL should let us complete the implementation of this: https://go-review.googlesource.com/c/sys/+/176625.
The actual part where we find and enumerate pipes is done, just need to put the appropriate token code in place to access it now.
Pending the merging of that CL, here's some working code:
package main
import (
"errors"
"fmt"
"github.com/Microsoft/go-winio"
"golang.org/x/sys/windows"
"net"
"runtime"
"strings"
"unsafe"
)
func openPipe(tunnelName string) (net.Conn, error) {
runtime.LockOSThread()
defer func() {
windows.RevertToSelf()
runtime.UnlockOSThread()
}()
privileges := windows.Tokenprivileges{
PrivilegeCount: 1,
Privileges: [1]windows.LUIDAndAttributes{
{
Attributes: windows.SE_PRIVILEGE_ENABLED,
},
},
}
err := windows.LookupPrivilegeValue(nil, windows.StringToUTF16Ptr("SeDebugPrivilege"), &privileges.Privileges[0].Luid)
if err != nil {
return nil, err
}
err = windows.ImpersonateSelf(windows.SecurityImpersonation)
if err != nil {
return nil, err
}
thread, _ := windows.GetCurrentThread()
var threadToken windows.Token
err = windows.OpenThreadToken(thread, windows.TOKEN_ADJUST_PRIVILEGES, false, &threadToken)
if err != nil {
return nil, err
}
defer threadToken.Close()
err = windows.AdjustTokenPrivileges(threadToken, false, &privileges, uint32(unsafe.Sizeof(privileges)), nil, nil)
if err != nil {
return nil, err
}
processes, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
if err != nil {
return nil, err
}
defer windows.CloseHandle(processes)
processEntry := windows.ProcessEntry32{Size: uint32(unsafe.Sizeof(windows.ProcessEntry32{}))}
pid := uint32(0)
for err = windows.Process32First(processes, &processEntry); err == nil; err = windows.Process32Next(processes, &processEntry) {
if strings.ToLower(windows.UTF16ToString(processEntry.ExeFile[:])) == "winlogon.exe" {
pid = processEntry.ProcessID
break
}
}
if pid == 0 {
return nil, errors.New("Unable to find winlogon.exe process")
}
winlogonProcess, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, pid)
if err != nil {
return nil, err
}
defer windows.CloseHandle(winlogonProcess)
var winlogonToken windows.Token
err = windows.OpenProcessToken(winlogonProcess, windows.TOKEN_IMPERSONATE|windows.TOKEN_DUPLICATE, &winlogonToken)
if err != nil {
return nil, err
}
defer winlogonToken.Close()
var duplicatedToken windows.Token
err = windows.DuplicateTokenEx(winlogonToken, 0, nil, windows.SecurityImpersonation, windows.TokenImpersonation, &duplicatedToken)
if err != nil {
return nil, err
}
defer duplicatedToken.Close()
err = windows.SetThreadToken(nil, duplicatedToken)
if err != nil {
return nil, err
}
return winio.DialPipe(`\\.\pipe\WireGuard\`+tunnelName, nil)
}
func main() {
var findData windows.Win32finddata
findHandle, err := windows.FindFirstFile(windows.StringToUTF16Ptr(`\\.\pipe\*`), &findData)
defer windows.CloseHandle(findHandle)
if err != nil {
fmt.Println(err)
return
}
for {
name := windows.UTF16ToString(findData.FileName[:])
for strings.HasPrefix(name, `WireGuard\`) {
name = name[10:]
fmt.Printf("name=%s\n", name)
conn, err := openPipe(name)
if err != nil {
fmt.Println(err)
break
}
defer conn.Close()
conn.Write([]byte("get=1\n\n"))
var resp [0x10000]byte
n, err := conn.Read(resp[:])
if err != nil {
fmt.Println(err)
break
}
fmt.Print(string(resp[:n]))
break
}
err = windows.FindNextFile(findHandle, &findData)
if err != nil {
if err == windows.ERROR_NO_MORE_FILES {
break
}
fmt.Println(err)
break
}
}
}
The Windows port uses the userspace protocol but uses special pipes to pass configuration rather than a UNIX socket:
"\\\\.\\pipe\\WireGuard\\"+name
. There should be a way to detect all of these pipes and configure them.Jason also recommends looking at https://github.com/Microsoft/go-winio.