/
security.go
393 lines (359 loc) · 12.6 KB
/
security.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/*
* Copyright (c) 2014-2017 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the license is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gowin32
import (
"github.com/winlabs/gowin32/wrappers"
"syscall"
"unsafe"
)
type PrivilegeName string
const (
PrivilegeCreateToken PrivilegeName = wrappers.SE_CREATE_TOKEN_NAME
PrivilegeAssignPrimaryToken PrivilegeName = wrappers.SE_ASSIGNPRIMARYTOKEN_NAME
PrivilegeLockMemory PrivilegeName = wrappers.SE_LOCK_MEMORY_NAME
PrivilegeIncreaseQuota PrivilegeName = wrappers.SE_INCREASE_QUOTA_NAME
PrivilegeUnsolicitedInput PrivilegeName = wrappers.SE_UNSOLICITED_INPUT_NAME
PrivilegeMachineAccount PrivilegeName = wrappers.SE_MACHINE_ACCOUNT_NAME
PrivilegeTCB PrivilegeName = wrappers.SE_TCB_NAME
PrivilegeSecurity PrivilegeName = wrappers.SE_SECURITY_NAME
PrivilegeTakeOwnership PrivilegeName = wrappers.SE_TAKE_OWNERSHIP_NAME
PrivilegeLoadDriver PrivilegeName = wrappers.SE_LOAD_DRIVER_NAME
PrivilegeSystemProfile PrivilegeName = wrappers.SE_SYSTEM_PROFILE_NAME
PrivilegeSystemTime PrivilegeName = wrappers.SE_SYSTEMTIME_NAME
PrivilegeProfileSingleProcess PrivilegeName = wrappers.SE_PROF_SINGLE_PROCESS_NAME
PrivilegeIncreaseBasePriority PrivilegeName = wrappers.SE_INC_BASE_PRIORITY_NAME
PrivilegeCreatePagefile PrivilegeName = wrappers.SE_CREATE_PAGEFILE_NAME
PrivilegeCreatePermanent PrivilegeName = wrappers.SE_CREATE_PERMANENT_NAME
PrivilegeBackup PrivilegeName = wrappers.SE_BACKUP_NAME
PrivilegeRestore PrivilegeName = wrappers.SE_RESTORE_NAME
PrivilegeShutdown PrivilegeName = wrappers.SE_SHUTDOWN_NAME
PrivilegeDebug PrivilegeName = wrappers.SE_DEBUG_NAME
PrivilegeAudit PrivilegeName = wrappers.SE_AUDIT_NAME
PrivilegeSystemEnvironment PrivilegeName = wrappers.SE_SYSTEM_ENVIRONMENT_NAME
PrivilegeChangeNotify PrivilegeName = wrappers.SE_CHANGE_NOTIFY_NAME
PrivilegeRemoteShutdown PrivilegeName = wrappers.SE_REMOTE_SHUTDOWN_NAME
PrivilegeUndock PrivilegeName = wrappers.SE_UNDOCK_NAME
PrivilegeSyncAgent PrivilegeName = wrappers.SE_SYNC_AGENT_NAME
PrivilegeEnableDelegation PrivilegeName = wrappers.SE_ENABLE_DELEGATION_NAME
PrivilegeManageVolume PrivilegeName = wrappers.SE_MANAGE_VOLUME_NAME
PrivilegeImpersonate PrivilegeName = wrappers.SE_IMPERSONATE_NAME
PrivilegeCreateGlobal PrivilegeName = wrappers.SE_CREATE_GLOBAL_NAME
PrivilegeTrustedCredManAccess PrivilegeName = wrappers.SE_TRUSTED_CREDMAN_ACCESS_NAME
PrivilegeRelabel PrivilegeName = wrappers.SE_RELABEL_NAME
PrivilegeIncreaseWorkingSet PrivilegeName = wrappers.SE_INC_WORKING_SET_NAME
PrivilegeTimeZone PrivilegeName = wrappers.SE_TIME_ZONE_NAME
PrivilegeCreateSymbolicLink PrivilegeName = wrappers.SE_CREATE_SYMBOLIC_LINK_NAME
)
type Privilege struct {
luid wrappers.LUID
}
func GetPrivilege(name PrivilegeName) (*Privilege, error) {
var luid wrappers.LUID
err := wrappers.LookupPrivilegeValue(
nil,
syscall.StringToUTF16Ptr(string(name)),
&luid)
if err != nil {
return nil, NewWindowsError("LookupPrivilegeValue", err)
}
return &Privilege{luid: luid}, nil
}
type SecurityIDType int32
const (
SecurityIDTypeUser SecurityIDType = wrappers.SidTypeUser
SecurityIDTypeGroup SecurityIDType = wrappers.SidTypeGroup
SecurityIDTypeDomain SecurityIDType = wrappers.SidTypeDomain
SecurityIDTypeAlias SecurityIDType = wrappers.SidTypeAlias
SecurityIDTypeWellKnownGroup SecurityIDType = wrappers.SidTypeWellKnownGroup
SecurityIDTypeDeletedAccount SecurityIDType = wrappers.SidTypeDeletedAccount
SecurityIDTypeInvalid SecurityIDType = wrappers.SidTypeInvalid
SecurityIDTypeUnknown SecurityIDType = wrappers.SidTypeUnknown
SecurityIDTypeComputer SecurityIDType = wrappers.SidTypeComputer
SecurityIDTypeLabel SecurityIDType = wrappers.SidTypeLabel
)
type SecurityID struct {
sid *wrappers.SID
}
func (self SecurityID) GetLength() uint {
return uint(wrappers.GetLengthSid(self.sid))
}
func (self SecurityID) Copy() (SecurityID, error) {
length := self.GetLength()
buf := make([]byte, length)
sid := (*wrappers.SID)(unsafe.Pointer(&buf[0]))
err := wrappers.CopySid(uint32(length), sid, self.sid)
if err != nil {
return SecurityID{}, NewWindowsError("CopySid", err)
}
return SecurityID{sid}, nil
}
func (self SecurityID) Equal(other SecurityID) bool {
return wrappers.EqualSid(self.sid, other.sid)
}
func (self SecurityID) String() (string, error) {
var stringSid *uint16
if err := wrappers.ConvertSidToStringSid(self.sid, &stringSid); err != nil {
return "", NewWindowsError("ConvertSidToStringSid", err)
}
defer wrappers.LocalFree(syscall.Handle(unsafe.Pointer(stringSid)))
return LpstrToString(stringSid), nil
}
type WellKnownSecurityIDType int32
const (
WellKnownSecurityIDLocalSystem WellKnownSecurityIDType = wrappers.WinLocalSystemSid
WellKnownSecurityIDLocalService WellKnownSecurityIDType = wrappers.WinLocalServiceSid
WellKnownSecurityIDNetworkService WellKnownSecurityIDType = wrappers.WinNetworkServiceSid
)
func GetWellKnownSecurityID(wellKnownType WellKnownSecurityIDType) (SecurityID, error) {
var needed uint32
err := wrappers.CreateWellKnownSid(int32(wellKnownType), nil, nil, &needed)
if err != nil && err != wrappers.ERROR_INSUFFICIENT_BUFFER {
return SecurityID{}, NewWindowsError("CreateWellKnownSid", err)
}
buf := make([]byte, needed)
sid := (*wrappers.SID)(unsafe.Pointer(&buf[0]))
if err := wrappers.CreateWellKnownSid(int32(wellKnownType), nil, sid, &needed); err != nil {
return SecurityID{}, NewWindowsError("CreateWellKnownSid", err)
}
return SecurityID{sid}, nil
}
func GetFileOwner(path string) (SecurityID, error) {
var needed uint32
wrappers.GetFileSecurity(
syscall.StringToUTF16Ptr(path),
wrappers.OWNER_SECURITY_INFORMATION,
nil,
0,
&needed)
buf := make([]byte, needed)
err := wrappers.GetFileSecurity(
syscall.StringToUTF16Ptr(path),
wrappers.OWNER_SECURITY_INFORMATION,
&buf[0],
needed,
&needed)
if err != nil {
return SecurityID{}, NewWindowsError("GetFileSecurity", err)
}
var ownerSid *wrappers.SID
if err := wrappers.GetSecurityDescriptorOwner(&buf[0], &ownerSid, nil); err != nil {
return SecurityID{}, NewWindowsError("GetSecurityDescriptorOwner", err)
}
return SecurityID{ownerSid}, nil
}
func SetFileOwner(path string, sid SecurityID) error {
sd := make([]byte, wrappers.SECURITY_DESCRIPTOR_MIN_LENGTH)
if err := wrappers.InitializeSecurityDescriptor(&sd[0], wrappers.SECURITY_DESCRIPTOR_REVISION); err != nil {
return NewWindowsError("InitializeSecurityDescriptor", err)
}
if err := wrappers.SetSecurityDescriptorOwner(&sd[0], sid.sid, false); err != nil {
return NewWindowsError("SetSecurityDescriptorOwner", err)
}
err := wrappers.SetFileSecurity(
syscall.StringToUTF16Ptr(path),
wrappers.OWNER_SECURITY_INFORMATION,
&sd[0])
if err != nil {
return NewWindowsError("SetFileSecurity", err)
}
return nil
}
func GetLocalAccountByName(accountName string) (SecurityID, string, SecurityIDType, error) {
var neededForSid uint32
var neededForDomain uint32
var use int32
err := wrappers.LookupAccountName(
nil,
syscall.StringToUTF16Ptr(accountName),
nil,
&neededForSid,
nil,
&neededForDomain,
&use)
if err != nil && err != wrappers.ERROR_INSUFFICIENT_BUFFER {
return SecurityID{}, "", 0, NewWindowsError("LookupAccountName", err)
}
sidBuf := make([]byte, neededForSid)
sid := (*wrappers.SID)(unsafe.Pointer(&sidBuf[0]))
domainBuf := make([]uint16, neededForDomain)
err = wrappers.LookupAccountName(
nil,
syscall.StringToUTF16Ptr(accountName),
sid,
&neededForSid,
&domainBuf[0],
&neededForDomain,
&use)
if err != nil {
return SecurityID{}, "", 0, NewWindowsError("LookupAccountName", err)
}
return SecurityID{sid}, syscall.UTF16ToString(domainBuf), SecurityIDType(use), nil
}
func BeginImpersonateSelf() error {
if err := wrappers.ImpersonateSelf(wrappers.SecurityImpersonation); err != nil {
return NewWindowsError("ImpersonateSelf", nil)
}
return nil
}
func EndImpersonate() error {
if err := wrappers.RevertToSelf(); err != nil {
return NewWindowsError("RevertToSelf", nil)
}
return nil
}
type Token struct {
handle syscall.Handle
}
func OpenCurrentProcessToken() (*Token, error) {
return OpenCurrentProcessTokenWithAccess(wrappers.TOKEN_QUERY)
}
func OpenCurrentProcessTokenWithAccess(desiredAccess uint32) (*Token, error) {
hProcess := wrappers.GetCurrentProcess()
var hToken syscall.Handle
if err := wrappers.OpenProcessToken(hProcess, desiredAccess, &hToken); err != nil {
return nil, NewWindowsError("OpenProcessToken", err)
}
return &Token{handle: hToken}, nil
}
func OpenOtherProcessToken(pid uint) (*Token, error) {
hProcess, err := wrappers.OpenProcess(wrappers.PROCESS_QUERY_INFORMATION, false, uint32(pid))
if err != nil {
return nil, NewWindowsError("OpenProcess", err)
}
defer syscall.CloseHandle(hProcess)
var hToken syscall.Handle
if err := wrappers.OpenProcessToken(hProcess, wrappers.TOKEN_QUERY, &hToken); err != nil {
return nil, NewWindowsError("OpenProcessToken", err)
}
return &Token{handle: hToken}, nil
}
func OpenCurrentThreadToken(openAsSelf bool) (*Token, error) {
hThread := wrappers.GetCurrentThread()
var hToken syscall.Handle
err := wrappers.OpenThreadToken(
hThread,
wrappers.TOKEN_QUERY|wrappers.TOKEN_ADJUST_PRIVILEGES,
openAsSelf,
&hToken)
if err != nil {
return nil, NewWindowsError("OpenThreadToken", err)
}
return &Token{handle: hToken}, nil
}
func (self *Token) Close() error {
if self.handle != 0 {
if err := wrappers.CloseHandle(self.handle); err != nil {
return NewWindowsError("CloseHandle", err)
}
self.handle = 0
}
return nil
}
func (self *Token) EnablePrivilege(privilege *Privilege, enable bool) error {
tokenPrivileges := wrappers.TOKEN_PRIVILEGES{
PrivilegeCount: 1,
Privileges: [1]wrappers.LUID_AND_ATTRIBUTES{
{Luid: privilege.luid},
},
}
if enable {
tokenPrivileges.Privileges[0].Attributes = wrappers.SE_PRIVILEGE_ENABLED
}
if err := wrappers.AdjustTokenPrivileges(self.handle, false, &tokenPrivileges, 0, nil, nil); err != nil {
return NewWindowsError("AdjustTokenPrivileges", err)
}
return nil
}
func (self *Token) GetUser() (SecurityID, error) {
var needed uint32
err := wrappers.GetTokenInformation(
self.handle,
wrappers.TokenUser,
nil,
0,
&needed)
if err != nil && err != wrappers.ERROR_INSUFFICIENT_BUFFER {
return SecurityID{}, NewWindowsError("GetTokenInformation", err)
}
buf := make([]byte, needed)
err = wrappers.GetTokenInformation(
self.handle,
wrappers.TokenUser,
&buf[0],
needed,
&needed)
if err != nil {
return SecurityID{}, NewWindowsError("GetTokenInformation", err)
}
userData := (*wrappers.TOKEN_USER)(unsafe.Pointer(&buf[0]))
sid, err := SecurityID{userData.User.Sid}.Copy()
if err != nil {
return SecurityID{}, err
}
return sid, nil
}
func (self *Token) GetOwner() (SecurityID, error) {
var needed uint32
err := wrappers.GetTokenInformation(
self.handle,
wrappers.TokenOwner,
nil,
0,
&needed)
if err != nil && err != wrappers.ERROR_INSUFFICIENT_BUFFER {
return SecurityID{}, NewWindowsError("GetTokenInformation", err)
}
buf := make([]byte, needed)
err = wrappers.GetTokenInformation(
self.handle,
wrappers.TokenOwner,
&buf[0],
needed,
&needed)
if err != nil {
return SecurityID{}, NewWindowsError("GetTokenInformation", err)
}
ownerData := (*wrappers.TOKEN_OWNER)(unsafe.Pointer(&buf[0]))
sid, err := SecurityID{ownerData.Owner}.Copy()
if err != nil {
return SecurityID{}, err
}
return sid, nil
}
func IsAdmin() (bool, error) {
var sid *wrappers.SID
err := wrappers.AllocateAndInitializeSid(
&wrappers.SECURITY_NT_AUTHORITY,
2,
wrappers.SECURITY_BUILTIN_DOMAIN_RID,
wrappers.DOMAIN_ALIAS_RID_ADMINS,
0,
0,
0,
0,
0,
0,
&sid)
if err != nil {
return false, NewWindowsError("AllocateAndInitializeSid", err)
}
defer wrappers.FreeSid(sid)
var isAdmin bool
if err := wrappers.CheckTokenMembership(0, sid, &isAdmin); err != nil {
return false, NewWindowsError("CheckTokenMembership", err)
}
return isAdmin, nil
}