github.com/elastic/gosigar@v0.14.3/sys/windows/privileges.go (about) 1 // +build windows 2 3 package windows 4 5 import ( 6 "bytes" 7 "encoding/binary" 8 "encoding/json" 9 "fmt" 10 "runtime" 11 "strings" 12 "sync" 13 "syscall" 14 15 "github.com/pkg/errors" 16 "golang.org/x/sys/windows" 17 ) 18 19 // Cache of privilege names to LUIDs. 20 var ( 21 privNames = make(map[string]int64) 22 privNameMutex sync.Mutex 23 ) 24 25 const ( 26 // SeDebugPrivilege is the name of the privilege used to debug programs. 27 SeDebugPrivilege = "SeDebugPrivilege" 28 ) 29 30 // Errors returned by AdjustTokenPrivileges. 31 const ( 32 ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 33 ) 34 35 // Attribute bits for privileges. 36 const ( 37 _SE_PRIVILEGE_ENABLED_BY_DEFAULT uint32 = 0x00000001 38 _SE_PRIVILEGE_ENABLED uint32 = 0x00000002 39 _SE_PRIVILEGE_REMOVED uint32 = 0x00000004 40 _SE_PRIVILEGE_USED_FOR_ACCESS uint32 = 0x80000000 41 ) 42 43 // Privilege contains information about a single privilege associated with a 44 // Token. 45 type Privilege struct { 46 LUID int64 `json:"-"` // Locally unique identifier (guaranteed only until the system is restarted). 47 Name string `json:"-"` 48 EnabledByDefault bool `json:"enabled_by_default,omitempty"` 49 Enabled bool `json:"enabled"` 50 Removed bool `json:"removed,omitempty"` 51 Used bool `json:"used,omitempty"` 52 } 53 54 func (p Privilege) String() string { 55 var buf bytes.Buffer 56 buf.WriteString(p.Name) 57 buf.WriteString("=(") 58 59 opts := make([]string, 0, 4) 60 if p.EnabledByDefault { 61 opts = append(opts, "Default") 62 } 63 if p.Enabled { 64 opts = append(opts, "Enabled") 65 } 66 if !p.EnabledByDefault && !p.Enabled { 67 opts = append(opts, "Disabled") 68 } 69 if p.Removed { 70 opts = append(opts, "Removed") 71 } 72 if p.Used { 73 opts = append(opts, "Used") 74 } 75 76 buf.WriteString(strings.Join(opts, ", ")) 77 buf.WriteString(")") 78 79 // Example: SeDebugPrivilege=(Default, Enabled) 80 return buf.String() 81 } 82 83 // User represent the information about a Windows account. 84 type User struct { 85 SID string 86 Account string 87 Domain string 88 Type uint32 89 } 90 91 func (u User) String() string { 92 return fmt.Sprintf(`User:%v\%v, SID:%v, Type:%v`, u.Domain, u.Account, u.SID, u.Type) 93 } 94 95 // DebugInfo contains general debug info about the current process. 96 type DebugInfo struct { 97 OSVersion Version // OS version info. 98 Arch string // Architecture of the machine. 99 NumCPU int // Number of CPUs. 100 User User // User that this process is running as. 101 ProcessPrivs map[string]Privilege // Privileges held by the process. 102 } 103 104 func (d DebugInfo) String() string { 105 bytes, _ := json.Marshal(d) 106 return string(bytes) 107 } 108 109 // LookupPrivilegeName looks up a privilege name given a LUID value. 110 func LookupPrivilegeName(systemName string, luid int64) (string, error) { 111 buf := make([]uint16, 256) 112 bufSize := uint32(len(buf)) 113 err := _LookupPrivilegeName(systemName, &luid, &buf[0], &bufSize) 114 if err != nil { 115 return "", errors.Wrapf(err, "LookupPrivilegeName failed for luid=%v", luid) 116 } 117 118 return syscall.UTF16ToString(buf), nil 119 } 120 121 // mapPrivileges maps privilege names to LUID values. 122 func mapPrivileges(names []string) ([]int64, error) { 123 var privileges []int64 124 privNameMutex.Lock() 125 defer privNameMutex.Unlock() 126 for _, name := range names { 127 p, ok := privNames[name] 128 if !ok { 129 err := _LookupPrivilegeValue("", name, &p) 130 if err != nil { 131 return nil, errors.Wrapf(err, "LookupPrivilegeValue failed on '%v'", name) 132 } 133 privNames[name] = p 134 } 135 privileges = append(privileges, p) 136 } 137 return privileges, nil 138 } 139 140 // EnableTokenPrivileges enables the specified privileges in the given 141 // Token. The token must have TOKEN_ADJUST_PRIVILEGES access. If the token 142 // does not already contain the privilege it cannot be enabled. 143 func EnableTokenPrivileges(token syscall.Token, privileges ...string) error { 144 privValues, err := mapPrivileges(privileges) 145 if err != nil { 146 return err 147 } 148 149 var b bytes.Buffer 150 binary.Write(&b, binary.LittleEndian, uint32(len(privValues))) 151 for _, p := range privValues { 152 binary.Write(&b, binary.LittleEndian, p) 153 binary.Write(&b, binary.LittleEndian, uint32(_SE_PRIVILEGE_ENABLED)) 154 } 155 156 success, err := _AdjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(b.Len()), nil, nil) 157 if !success { 158 return err 159 } 160 if err == ERROR_NOT_ALL_ASSIGNED { 161 return errors.Wrap(err, "error not all privileges were assigned") 162 } 163 164 return nil 165 } 166 167 // GetTokenPrivileges returns a list of privileges associated with a token. 168 // The provided token must have at a minimum TOKEN_QUERY access. This is a 169 // wrapper around the GetTokenInformation function. 170 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx 171 func GetTokenPrivileges(token syscall.Token) (map[string]Privilege, error) { 172 // Determine the required buffer size. 173 var size uint32 174 syscall.GetTokenInformation(token, syscall.TokenPrivileges, nil, 0, &size) 175 176 // This buffer will receive a TOKEN_PRIVILEGE structure. 177 b := bytes.NewBuffer(make([]byte, size)) 178 err := syscall.GetTokenInformation(token, syscall.TokenPrivileges, &b.Bytes()[0], uint32(b.Len()), &size) 179 if err != nil { 180 return nil, errors.Wrap(err, "GetTokenInformation failed") 181 } 182 183 var privilegeCount uint32 184 err = binary.Read(b, binary.LittleEndian, &privilegeCount) 185 if err != nil { 186 return nil, errors.Wrap(err, "failed to read PrivilegeCount") 187 } 188 189 rtn := make(map[string]Privilege, privilegeCount) 190 for i := 0; i < int(privilegeCount); i++ { 191 var luid int64 192 err = binary.Read(b, binary.LittleEndian, &luid) 193 if err != nil { 194 return nil, errors.Wrap(err, "failed to read LUID value") 195 } 196 197 var attributes uint32 198 err = binary.Read(b, binary.LittleEndian, &attributes) 199 if err != nil { 200 return nil, errors.Wrap(err, "failed to read attributes") 201 } 202 203 name, err := LookupPrivilegeName("", luid) 204 if err != nil { 205 return nil, errors.Wrapf(err, "LookupPrivilegeName failed for LUID=%v", luid) 206 } 207 208 rtn[name] = Privilege{ 209 LUID: luid, 210 Name: name, 211 EnabledByDefault: (attributes & _SE_PRIVILEGE_ENABLED_BY_DEFAULT) > 0, 212 Enabled: (attributes & _SE_PRIVILEGE_ENABLED) > 0, 213 Removed: (attributes & _SE_PRIVILEGE_REMOVED) > 0, 214 Used: (attributes & _SE_PRIVILEGE_USED_FOR_ACCESS) > 0, 215 } 216 } 217 218 return rtn, nil 219 } 220 221 // GetTokenUser returns the User associated with the given Token. 222 func GetTokenUser(token syscall.Token) (User, error) { 223 tokenUser, err := token.GetTokenUser() 224 if err != nil { 225 return User{}, errors.Wrap(err, "GetTokenUser failed") 226 } 227 228 var user User 229 user.SID, err = tokenUser.User.Sid.String() 230 if err != nil { 231 return user, errors.Wrap(err, "ConvertSidToStringSid failed") 232 } 233 234 user.Account, user.Domain, user.Type, err = tokenUser.User.Sid.LookupAccount("") 235 if err != nil { 236 return user, errors.Wrap(err, "LookupAccountSid failed") 237 } 238 239 return user, nil 240 } 241 242 // GetDebugInfo returns general debug info about the current process. 243 func GetDebugInfo() (*DebugInfo, error) { 244 h, err := windows.GetCurrentProcess() 245 if err != nil { 246 return nil, err 247 } 248 249 var token syscall.Token 250 err = syscall.OpenProcessToken(syscall.Handle(h), syscall.TOKEN_QUERY, &token) 251 if err != nil { 252 return nil, err 253 } 254 255 privs, err := GetTokenPrivileges(token) 256 if err != nil { 257 return nil, err 258 } 259 260 user, err := GetTokenUser(token) 261 if err != nil { 262 return nil, err 263 } 264 265 return &DebugInfo{ 266 User: user, 267 ProcessPrivs: privs, 268 OSVersion: GetWindowsVersion(), 269 Arch: runtime.GOARCH, 270 NumCPU: runtime.NumCPU(), 271 }, nil 272 }