1// +build windows
2
3package winio
4
5import (
6	"syscall"
7	"unsafe"
8)
9
10//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
11//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
12//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
13//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
14//sys localFree(mem uintptr) = LocalFree
15//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
16
17const (
18	cERROR_NONE_MAPPED = syscall.Errno(1332)
19)
20
21type AccountLookupError struct {
22	Name string
23	Err  error
24}
25
26func (e *AccountLookupError) Error() string {
27	if e.Name == "" {
28		return "lookup account: empty account name specified"
29	}
30	var s string
31	switch e.Err {
32	case cERROR_NONE_MAPPED:
33		s = "not found"
34	default:
35		s = e.Err.Error()
36	}
37	return "lookup account " + e.Name + ": " + s
38}
39
40type SddlConversionError struct {
41	Sddl string
42	Err  error
43}
44
45func (e *SddlConversionError) Error() string {
46	return "convert " + e.Sddl + ": " + e.Err.Error()
47}
48
49// LookupSidByName looks up the SID of an account by name
50func LookupSidByName(name string) (sid string, err error) {
51	if name == "" {
52		return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
53	}
54
55	var sidSize, sidNameUse, refDomainSize uint32
56	err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
57	if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
58		return "", &AccountLookupError{name, err}
59	}
60	sidBuffer := make([]byte, sidSize)
61	refDomainBuffer := make([]uint16, refDomainSize)
62	err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
63	if err != nil {
64		return "", &AccountLookupError{name, err}
65	}
66	var strBuffer *uint16
67	err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
68	if err != nil {
69		return "", &AccountLookupError{name, err}
70	}
71	sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
72	localFree(uintptr(unsafe.Pointer(strBuffer)))
73	return sid, nil
74}
75
76func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
77	var sdBuffer uintptr
78	err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
79	if err != nil {
80		return nil, &SddlConversionError{sddl, err}
81	}
82	defer localFree(sdBuffer)
83	sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
84	copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
85	return sd, nil
86}
87
88func SecurityDescriptorToSddl(sd []byte) (string, error) {
89	var sddl *uint16
90	// The returned string length seems to including an aribtrary number of terminating NULs.
91	// Don't use it.
92	err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
93	if err != nil {
94		return "", err
95	}
96	defer localFree(uintptr(unsafe.Pointer(sddl)))
97	return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
98}
99