Source file
src/os/user/lookup_windows.go
1
2
3
4
5 package user
6
7 import (
8 "errors"
9 "fmt"
10 "internal/syscall/windows"
11 "internal/syscall/windows/registry"
12 "runtime"
13 "syscall"
14 "unsafe"
15 )
16
17 func isDomainJoined() (bool, error) {
18 var domain *uint16
19 var status uint32
20 err := syscall.NetGetJoinInformation(nil, &domain, &status)
21 if err != nil {
22 return false, err
23 }
24 syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain)))
25 return status == syscall.NetSetupDomainName, nil
26 }
27
28 func lookupFullNameDomain(domainAndUser string) (string, error) {
29 return syscall.TranslateAccountName(domainAndUser,
30 syscall.NameSamCompatible, syscall.NameDisplay, 50)
31 }
32
33 func lookupFullNameServer(servername, username string) (string, error) {
34 s, e := syscall.UTF16PtrFromString(servername)
35 if e != nil {
36 return "", e
37 }
38 u, e := syscall.UTF16PtrFromString(username)
39 if e != nil {
40 return "", e
41 }
42 var p *byte
43 e = syscall.NetUserGetInfo(s, u, 10, &p)
44 if e != nil {
45 return "", e
46 }
47 defer syscall.NetApiBufferFree(p)
48 i := (*syscall.UserInfo10)(unsafe.Pointer(p))
49 return windows.UTF16PtrToString(i.FullName), nil
50 }
51
52 func lookupFullName(domain, username, domainAndUser string) (string, error) {
53 joined, err := isDomainJoined()
54 if err == nil && joined {
55 name, err := lookupFullNameDomain(domainAndUser)
56 if err == nil {
57 return name, nil
58 }
59 }
60 name, err := lookupFullNameServer(domain, username)
61 if err == nil {
62 return name, nil
63 }
64
65
66
67 return username, nil
68 }
69
70
71
72 func getProfilesDirectory() (string, error) {
73 n := uint32(100)
74 for {
75 b := make([]uint16, n)
76 e := windows.GetProfilesDirectory(&b[0], &n)
77 if e == nil {
78 return syscall.UTF16ToString(b), nil
79 }
80 if e != syscall.ERROR_INSUFFICIENT_BUFFER {
81 return "", e
82 }
83 if n <= uint32(len(b)) {
84 return "", e
85 }
86 }
87 }
88
89
90 func lookupUsernameAndDomain(usid *syscall.SID) (username, domain string, e error) {
91 username, domain, t, e := usid.LookupAccount("")
92 if e != nil {
93 return "", "", e
94 }
95 if t != syscall.SidTypeUser {
96 return "", "", fmt.Errorf("user: should be user account type, not %d", t)
97 }
98 return username, domain, nil
99 }
100
101
102 func findHomeDirInRegistry(uid string) (dir string, e error) {
103 k, e := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\`+uid, registry.QUERY_VALUE)
104 if e != nil {
105 return "", e
106 }
107 defer k.Close()
108 dir, _, e = k.GetStringValue("ProfileImagePath")
109 if e != nil {
110 return "", e
111 }
112 return dir, nil
113 }
114
115
116 func lookupGroupName(groupname string) (string, error) {
117 sid, _, t, e := syscall.LookupSID("", groupname)
118 if e != nil {
119 return "", e
120 }
121
122
123
124
125
126
127 if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias {
128 return "", fmt.Errorf("lookupGroupName: should be group account type, not %d", t)
129 }
130 return sid.String()
131 }
132
133
134
135 func listGroupsForUsernameAndDomain(username, domain string) ([]string, error) {
136
137 var query string
138 joined, err := isDomainJoined()
139 if err == nil && joined && len(domain) != 0 {
140 query = domain + `\` + username
141 } else {
142 query = username
143 }
144 q, err := syscall.UTF16PtrFromString(query)
145 if err != nil {
146 return nil, err
147 }
148 var p0 *byte
149 var entriesRead, totalEntries uint32
150
151
152
153
154 err = windows.NetUserGetLocalGroups(nil, q, 0, windows.LG_INCLUDE_INDIRECT, &p0, windows.MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries)
155 if err != nil {
156 return nil, err
157 }
158 defer syscall.NetApiBufferFree(p0)
159 if entriesRead == 0 {
160 return nil, nil
161 }
162 entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead:entriesRead]
163 var sids []string
164 for _, entry := range entries {
165 if entry.Name == nil {
166 continue
167 }
168 sid, err := lookupGroupName(windows.UTF16PtrToString(entry.Name))
169 if err != nil {
170 return nil, err
171 }
172 sids = append(sids, sid)
173 }
174 return sids, nil
175 }
176
177 func newUser(uid, gid, dir, username, domain string) (*User, error) {
178 domainAndUser := domain + `\` + username
179 name, e := lookupFullName(domain, username, domainAndUser)
180 if e != nil {
181 return nil, e
182 }
183 u := &User{
184 Uid: uid,
185 Gid: gid,
186 Username: domainAndUser,
187 Name: name,
188 HomeDir: dir,
189 }
190 return u, nil
191 }
192
193 var (
194
195
196 userBuffer = 0
197 groupBuffer = 0
198 )
199
200 func current() (*User, error) {
201
202
203
204 var usr *User
205 err := runAsProcessOwner(func() error {
206 t, e := syscall.OpenCurrentProcessToken()
207 if e != nil {
208 return e
209 }
210 defer t.Close()
211 u, e := t.GetTokenUser()
212 if e != nil {
213 return e
214 }
215 pg, e := t.GetTokenPrimaryGroup()
216 if e != nil {
217 return e
218 }
219 uid, e := u.User.Sid.String()
220 if e != nil {
221 return e
222 }
223 gid, e := pg.PrimaryGroup.String()
224 if e != nil {
225 return e
226 }
227 dir, e := t.GetUserProfileDirectory()
228 if e != nil {
229 return e
230 }
231 username, e := windows.GetUserName(syscall.NameSamCompatible)
232 if e != nil {
233 return e
234 }
235 displayName, e := windows.GetUserName(syscall.NameDisplay)
236 if e != nil {
237
238
239 displayName = username
240 }
241 usr = &User{
242 Uid: uid,
243 Gid: gid,
244 Username: username,
245 Name: displayName,
246 HomeDir: dir,
247 }
248 return nil
249 })
250 return usr, err
251 }
252
253
254
255
256 func runAsProcessOwner(f func() error) error {
257 var impersonationRollbackErr error
258 runtime.LockOSThread()
259 defer func() {
260
261
262
263 if impersonationRollbackErr != nil {
264 println("os/user: failed to revert to previous token:", impersonationRollbackErr.Error())
265 runtime.Goexit()
266 } else {
267 runtime.UnlockOSThread()
268 }
269 }()
270 prevToken, isProcessToken, err := getCurrentToken()
271 if err != nil {
272 return fmt.Errorf("os/user: failed to get current token: %w", err)
273 }
274 defer prevToken.Close()
275 if !isProcessToken {
276 if err = windows.RevertToSelf(); err != nil {
277 return fmt.Errorf("os/user: failed to revert to self: %w", err)
278 }
279 defer func() {
280 impersonationRollbackErr = windows.ImpersonateLoggedOnUser(prevToken)
281 }()
282 }
283 return f()
284 }
285
286
287
288 func getCurrentToken() (t syscall.Token, isProcessToken bool, err error) {
289 thread, _ := windows.GetCurrentThread()
290
291 err = windows.OpenThreadToken(thread, syscall.TOKEN_QUERY|syscall.TOKEN_DUPLICATE|syscall.TOKEN_IMPERSONATE, true, &t)
292 if errors.Is(err, windows.ERROR_NO_TOKEN) {
293
294 isProcessToken = true
295 t, err = syscall.OpenCurrentProcessToken()
296 }
297 return t, isProcessToken, err
298 }
299
300
301
302
303 func lookupUserPrimaryGroup(username, domain string) (string, error) {
304
305 sid, _, t, e := syscall.LookupSID("", domain)
306 if e != nil {
307 return "", e
308 }
309 if t != syscall.SidTypeDomain {
310 return "", fmt.Errorf("lookupUserPrimaryGroup: should be domain account type, not %d", t)
311 }
312 domainRID, e := sid.String()
313 if e != nil {
314 return "", e
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329 joined, err := isDomainJoined()
330 if err == nil && joined {
331 return domainRID + "-513", nil
332 }
333
334
335
336
337
338
339 u, e := syscall.UTF16PtrFromString(username)
340 if e != nil {
341 return "", e
342 }
343 d, e := syscall.UTF16PtrFromString(domain)
344 if e != nil {
345 return "", e
346 }
347 var p *byte
348 e = syscall.NetUserGetInfo(d, u, 4, &p)
349 if e != nil {
350 return "", e
351 }
352 defer syscall.NetApiBufferFree(p)
353 i := (*windows.UserInfo4)(unsafe.Pointer(p))
354 return fmt.Sprintf("%s-%d", domainRID, i.PrimaryGroupID), nil
355 }
356
357 func newUserFromSid(usid *syscall.SID) (*User, error) {
358 username, domain, e := lookupUsernameAndDomain(usid)
359 if e != nil {
360 return nil, e
361 }
362 gid, e := lookupUserPrimaryGroup(username, domain)
363 if e != nil {
364 return nil, e
365 }
366 uid, e := usid.String()
367 if e != nil {
368 return nil, e
369 }
370
371
372
373
374
375
376
377
378
379 dir, e := findHomeDirInRegistry(uid)
380 if e != nil {
381
382
383
384
385 dir, e = getProfilesDirectory()
386 if e != nil {
387 return nil, e
388 }
389 dir += `\` + username
390 }
391 return newUser(uid, gid, dir, username, domain)
392 }
393
394 func lookupUser(username string) (*User, error) {
395 sid, _, t, e := syscall.LookupSID("", username)
396 if e != nil {
397 return nil, e
398 }
399 if t != syscall.SidTypeUser {
400 return nil, fmt.Errorf("user: should be user account type, not %d", t)
401 }
402 return newUserFromSid(sid)
403 }
404
405 func lookupUserId(uid string) (*User, error) {
406 sid, e := syscall.StringToSid(uid)
407 if e != nil {
408 return nil, e
409 }
410 return newUserFromSid(sid)
411 }
412
413 func lookupGroup(groupname string) (*Group, error) {
414 sid, err := lookupGroupName(groupname)
415 if err != nil {
416 return nil, err
417 }
418 return &Group{Name: groupname, Gid: sid}, nil
419 }
420
421 func lookupGroupId(gid string) (*Group, error) {
422 sid, err := syscall.StringToSid(gid)
423 if err != nil {
424 return nil, err
425 }
426 groupname, _, t, err := sid.LookupAccount("")
427 if err != nil {
428 return nil, err
429 }
430 if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias {
431 return nil, fmt.Errorf("lookupGroupId: should be group account type, not %d", t)
432 }
433 return &Group{Name: groupname, Gid: gid}, nil
434 }
435
436 func listGroups(user *User) ([]string, error) {
437 var sids []string
438 if u, err := Current(); err == nil && u.Uid == user.Uid {
439
440
441 err := runAsProcessOwner(func() error {
442 t, err := syscall.OpenCurrentProcessToken()
443 if err != nil {
444 return err
445 }
446 defer t.Close()
447 groups, err := windows.GetTokenGroups(t)
448 if err != nil {
449 return err
450 }
451 for _, g := range groups.AllGroups() {
452 sid, err := g.Sid.String()
453 if err != nil {
454 return err
455 }
456 sids = append(sids, sid)
457 }
458 return nil
459 })
460 if err != nil {
461 return nil, err
462 }
463 } else {
464 sid, err := syscall.StringToSid(user.Uid)
465 if err != nil {
466 return nil, err
467 }
468 username, domain, err := lookupUsernameAndDomain(sid)
469 if err != nil {
470 return nil, err
471 }
472 sids, err = listGroupsForUsernameAndDomain(username, domain)
473 if err != nil {
474 return nil, err
475 }
476 }
477
478
479 for _, sid := range sids {
480 if sid == user.Gid {
481 return sids, nil
482 }
483 }
484 return append(sids, user.Gid), nil
485 }
486
View as plain text