From 89bdf52a5bc106ea48ff68185a36ed9b7e10c1c6 Mon Sep 17 00:00:00 2001 From: John Starks Date: Sun, 31 Jan 2016 15:17:01 -0800 Subject: [PATCH] Add routine for looking up account names Signed-off-by: John Starks --- pipe.go | 48 +++++++++++++++++-------------- sd.go | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sd_test.go | 26 +++++++++++++++++ syscall.go | 2 +- zsyscall.go | 50 +++++++++++++++++++++++++++++--- 5 files changed, 183 insertions(+), 26 deletions(-) create mode 100644 sd.go create mode 100644 sd_test.go diff --git a/pipe.go b/pipe.go index 438abd1..b281b5e 100644 --- a/pipe.go +++ b/pipe.go @@ -10,11 +10,15 @@ import ( ) //sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe -//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW -//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW +//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW +//sys createFile(name string, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW //sys waitNamedPipe(name string, timeout uint32) (err error) = WaitNamedPipeW -//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW -//sys localFree(mem uintptr) = LocalFree + +type securityAttributes struct { + Length uint32 + SecurityDescriptor *byte + InheritHandle uint32 +} const ( cERROR_PIPE_BUSY = syscall.Errno(231) @@ -127,31 +131,23 @@ type acceptResponse struct { type win32PipeListener struct { firstHandle syscall.Handle path string - securityDescriptor string + securityDescriptor []byte acceptCh chan (chan acceptResponse) closeCh chan int doneCh chan int } -func makeServerPipeHandle(path, securityDescriptor string, first bool) (syscall.Handle, error) { +func makeServerPipeHandle(path string, securityDescriptor []byte, first bool) (syscall.Handle, error) { var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED if first { flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE } - var sd uintptr - if securityDescriptor != "" { - err := convertStringSecurityDescriptorToSecurityDescriptor(securityDescriptor, 1, &sd, nil) - if err != nil { - return 0, err - } - } - var sa syscall.SecurityAttributes + var sa securityAttributes sa.Length = uint32(unsafe.Sizeof(sa)) - sa.SecurityDescriptor = sd - h, err := createNamedPipe(path, flags, cPIPE_REJECT_REMOTE_CLIENTS, cPIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa) - if sd != 0 { - localFree(sd) + if securityDescriptor != nil { + sa.SecurityDescriptor = &securityDescriptor[0] } + h, err := createNamedPipe(path, flags, cPIPE_REJECT_REMOTE_CLIENTS, cPIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa) if err != nil { return 0, &os.PathError{"open", path, err} } @@ -211,8 +207,18 @@ func (l *win32PipeListener) listenerRoutine() { close(l.doneCh) } -func ListenPipe(path, securityDescriptor string) (net.Listener, error) { - h, err := makeServerPipeHandle(path, securityDescriptor, true) +func ListenPipe(path, sddl string) (net.Listener, error) { + var ( + sd []byte + err error + ) + if sddl != "" { + sd, err = sddlToSecurityDescriptor(sddl) + if err != nil { + return nil, err + } + } + h, err := makeServerPipeHandle(path, sd, true) if err != nil { return nil, err } @@ -227,7 +233,7 @@ func ListenPipe(path, securityDescriptor string) (net.Listener, error) { l := &win32PipeListener{ firstHandle: h, path: path, - securityDescriptor: securityDescriptor, + securityDescriptor: sd, acceptCh: make(chan (chan acceptResponse)), closeCh: make(chan int), doneCh: make(chan int), diff --git a/sd.go b/sd.go new file mode 100644 index 0000000..c8bc7ff --- /dev/null +++ b/sd.go @@ -0,0 +1,83 @@ +package winio + +import ( + "syscall" + "unsafe" +) + +//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW +//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW +//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW +//sys localFree(mem uintptr) = LocalFree +//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength + +const ( + cERROR_NONE_MAPPED = syscall.Errno(1332) +) + +type AccountLookupError struct { + Name string + Err error +} + +func (e *AccountLookupError) Error() string { + if e.Name == "" { + return "lookup account: empty account name specified" + } + var s string + switch e.Err { + case cERROR_NONE_MAPPED: + s = "not found" + default: + s = e.Err.Error() + } + return "lookup account " + e.Name + ": " + s +} + +type SddlConversionError struct { + Sddl string + Err error +} + +func (e *SddlConversionError) Error() string { + return "convert " + e.Sddl + ": " + e.Err.Error() +} + +// LookupSidByName looks up the SID of an account by name +func LookupSidByName(name string) (sid string, err error) { + if name == "" { + return "", &AccountLookupError{name, cERROR_NONE_MAPPED} + } + + var sidSize, sidNameUse, refDomainSize uint32 + err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) + if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { + return "", &AccountLookupError{name, err} + } + sidBuffer := make([]byte, sidSize) + refDomainBuffer := make([]uint16, refDomainSize) + err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) + if err != nil { + return "", &AccountLookupError{name, err} + } + var strBuffer *uint16 + err = convertSidToStringSid(&sidBuffer[0], &strBuffer) + if err != nil { + return "", &AccountLookupError{name, err} + } + sid = syscall.UTF16ToString((*[1 << 30]uint16)(unsafe.Pointer(strBuffer))[:]) + localFree(uintptr(unsafe.Pointer(strBuffer))) + return sid, nil +} + +func sddlToSecurityDescriptor(sddl string) ([]byte, error) { + var sdBuffer uintptr + err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) + if err != nil { + return nil, &SddlConversionError{sddl, err} + } + defer localFree(sdBuffer) + sd := make([]byte, getSecurityDescriptorLength(sdBuffer)) + copy(sd, (*[1 << 30]byte)(unsafe.Pointer(sdBuffer))[:len(sd)]) + return sd, nil +} diff --git a/sd_test.go b/sd_test.go new file mode 100644 index 0000000..847db3c --- /dev/null +++ b/sd_test.go @@ -0,0 +1,26 @@ +package winio + +import "testing" + +func TestLookupInvalidSid(t *testing.T) { + _, err := LookupSidByName(".\\weoifjdsklfj") + aerr, ok := err.(*AccountLookupError) + if !ok || aerr.Err != cERROR_NONE_MAPPED { + t.Fatalf("expected AccountLookupError with ERROR_NONE_MAPPED, got %s", err) + } +} + +func TestLookupValidSid(t *testing.T) { + sid, err := LookupSidByName("Everyone") + if err != nil || sid != "S-1-1-0" { + t.Fatal("expected S-1-1-0, got %s, %s", sid, err) + } +} + +func TestLookupEmptyNameFails(t *testing.T) { + _, err := LookupSidByName(".\\weoifjdsklfj") + aerr, ok := err.(*AccountLookupError) + if !ok || aerr.Err != cERROR_NONE_MAPPED { + t.Fatalf("expected AccountLookupError with ERROR_NONE_MAPPED, got %s", err) + } +} diff --git a/syscall.go b/syscall.go index 2b6b3b7..20767dc 100644 --- a/syscall.go +++ b/syscall.go @@ -1,3 +1,3 @@ package winio -//go:generate go run mksyscall_windows.go -output zsyscall.go file.go pipe.go +//go:generate go run mksyscall_windows.go -output zsyscall.go file.go pipe.go sd.go diff --git a/zsyscall.go b/zsyscall.go index 5569bd1..bfe4ac3 100644 --- a/zsyscall.go +++ b/zsyscall.go @@ -19,8 +19,11 @@ var ( procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") procCreateFileW = modkernel32.NewProc("CreateFileW") procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") + procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") + procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") procLocalFree = modkernel32.NewProc("LocalFree") + procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") ) func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { @@ -84,7 +87,7 @@ func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { return } -func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { +func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(name) if err != nil { @@ -93,7 +96,7 @@ func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances ui return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa) } -func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { +func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) { r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) handle = syscall.Handle(r0) if handle == syscall.InvalidHandle { @@ -106,7 +109,7 @@ func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances return } -func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { +func createFile(name string, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(name) if err != nil { @@ -115,7 +118,7 @@ func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAtt return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile) } -func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { +func _createFile(name *uint16, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) handle = syscall.Handle(r0) if handle == syscall.InvalidHandle { @@ -149,6 +152,39 @@ func _waitNamedPipe(name *uint16, timeout uint32) (err error) { return } +func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(accountName) + if err != nil { + return + } + return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse) +} + +func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func convertSidToStringSid(sid *byte, str **uint16) (err error) { + r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(str) @@ -174,3 +210,9 @@ func localFree(mem uintptr) { syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0) return } + +func getSecurityDescriptorLength(sd uintptr) (len uint32) { + r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) + len = uint32(r0) + return +}