Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal/wguser: support Windows port's pipe interface #36

Closed
mdlayher opened this issue May 10, 2019 · 3 comments

Comments

2 participants
@mdlayher
Copy link
Member

commented May 10, 2019

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.

@mdlayher mdlayher changed the title internal/wgwindows: new package to support Windows port internal/wguser: support Windows port's pipe interface May 10, 2019

@zx2c4

This comment has been minimized.

@mdlayher

This comment has been minimized.

Copy link
Member Author

commented May 13, 2019

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.

@zx2c4

This comment has been minimized.

Copy link

commented May 13, 2019

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
		}
	}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.