github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/os/user/lookup_windows.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package user 6 7 import ( 8 "fmt" 9 "internal/syscall/windows" 10 "internal/syscall/windows/registry" 11 "syscall" 12 "unsafe" 13 ) 14 15 func isDomainJoined() (bool, error) { 16 var domain *uint16 17 var status uint32 18 err := syscall.NetGetJoinInformation(nil, &domain, &status) 19 if err != nil { 20 return false, err 21 } 22 syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain))) 23 return status == syscall.NetSetupDomainName, nil 24 } 25 26 func lookupFullNameDomain(domainAndUser string) (string, error) { 27 return syscall.TranslateAccountName(domainAndUser, 28 syscall.NameSamCompatible, syscall.NameDisplay, 50) 29 } 30 31 func lookupFullNameServer(servername, username string) (string, error) { 32 s, e := syscall.UTF16PtrFromString(servername) 33 if e != nil { 34 return "", e 35 } 36 u, e := syscall.UTF16PtrFromString(username) 37 if e != nil { 38 return "", e 39 } 40 var p *byte 41 e = syscall.NetUserGetInfo(s, u, 10, &p) 42 if e != nil { 43 return "", e 44 } 45 defer syscall.NetApiBufferFree(p) 46 i := (*syscall.UserInfo10)(unsafe.Pointer(p)) 47 if i.FullName == nil { 48 return "", nil 49 } 50 name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:]) 51 return name, nil 52 } 53 54 func lookupFullName(domain, username, domainAndUser string) (string, error) { 55 joined, err := isDomainJoined() 56 if err == nil && joined { 57 name, err := lookupFullNameDomain(domainAndUser) 58 if err == nil { 59 return name, nil 60 } 61 } 62 name, err := lookupFullNameServer(domain, username) 63 if err == nil { 64 return name, nil 65 } 66 // domain worked neither as a domain nor as a server 67 // could be domain server unavailable 68 // pretend username is fullname 69 return username, nil 70 } 71 72 // getProfilesDirectory retrieves the path to the root directory 73 // where user profiles are stored. 74 func getProfilesDirectory() (string, error) { 75 n := uint32(100) 76 for { 77 b := make([]uint16, n) 78 e := windows.GetProfilesDirectory(&b[0], &n) 79 if e == nil { 80 return syscall.UTF16ToString(b), nil 81 } 82 if e != syscall.ERROR_INSUFFICIENT_BUFFER { 83 return "", e 84 } 85 if n <= uint32(len(b)) { 86 return "", e 87 } 88 } 89 } 90 91 // lookupUsernameAndDomain obtains the username and domain for usid. 92 func lookupUsernameAndDomain(usid *syscall.SID) (username, domain string, e error) { 93 username, domain, t, e := usid.LookupAccount("") 94 if e != nil { 95 return "", "", e 96 } 97 if t != syscall.SidTypeUser { 98 return "", "", fmt.Errorf("user: should be user account type, not %d", t) 99 } 100 return username, domain, nil 101 } 102 103 // findHomeDirInRegistry finds the user home path based on the uid. 104 func findHomeDirInRegistry(uid string) (dir string, e error) { 105 k, e := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\`+uid, registry.QUERY_VALUE) 106 if e != nil { 107 return "", e 108 } 109 defer k.Close() 110 dir, _, e = k.GetStringValue("ProfileImagePath") 111 if e != nil { 112 return "", e 113 } 114 return dir, nil 115 } 116 117 // lookupGroupName accepts the name of a group and retrieves the group SID. 118 func lookupGroupName(groupname string) (string, error) { 119 sid, _, t, e := syscall.LookupSID("", groupname) 120 if e != nil { 121 return "", e 122 } 123 // https://msdn.microsoft.com/en-us/library/cc245478.aspx#gt_0387e636-5654-4910-9519-1f8326cf5ec0 124 // SidTypeAlias should also be treated as a group type next to SidTypeGroup 125 // and SidTypeWellKnownGroup: 126 // "alias object -> resource group: A group object..." 127 // 128 // Tests show that "Administrators" can be considered of type SidTypeAlias. 129 if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias { 130 return "", fmt.Errorf("lookupGroupName: should be group account type, not %d", t) 131 } 132 return sid.String() 133 } 134 135 // listGroupsForUsernameAndDomain accepts username and domain and retrieves 136 // a SID list of the local groups where this user is a member. 137 func listGroupsForUsernameAndDomain(username, domain string) ([]string, error) { 138 // Check if both the domain name and user should be used. 139 var query string 140 joined, err := isDomainJoined() 141 if err == nil && joined && len(domain) != 0 { 142 query = domain + `\` + username 143 } else { 144 query = username 145 } 146 q, err := syscall.UTF16PtrFromString(query) 147 if err != nil { 148 return nil, err 149 } 150 var p0 *byte 151 var entriesRead, totalEntries uint32 152 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa370655(v=vs.85).aspx 153 // NetUserGetLocalGroups() would return a list of LocalGroupUserInfo0 154 // elements which hold the names of local groups where the user participates. 155 // The list does not follow any sorting order. 156 // 157 // If no groups can be found for this user, NetUserGetLocalGroups() should 158 // always return the SID of a single group called "None", which 159 // also happens to be the primary group for the local user. 160 err = windows.NetUserGetLocalGroups(nil, q, 0, windows.LG_INCLUDE_INDIRECT, &p0, windows.MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries) 161 if err != nil { 162 return nil, err 163 } 164 defer syscall.NetApiBufferFree(p0) 165 if entriesRead == 0 { 166 return nil, fmt.Errorf("listGroupsForUsernameAndDomain: NetUserGetLocalGroups() returned an empty list for domain: %s, username: %s", domain, username) 167 } 168 entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead] 169 var sids []string 170 for _, entry := range entries { 171 if entry.Name == nil { 172 continue 173 } 174 name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(entry.Name))[:]) 175 sid, err := lookupGroupName(name) 176 if err != nil { 177 return nil, err 178 } 179 sids = append(sids, sid) 180 } 181 return sids, nil 182 } 183 184 func newUser(uid, gid, dir, username, domain string) (*User, error) { 185 domainAndUser := domain + `\` + username 186 name, e := lookupFullName(domain, username, domainAndUser) 187 if e != nil { 188 return nil, e 189 } 190 u := &User{ 191 Uid: uid, 192 Gid: gid, 193 Username: domainAndUser, 194 Name: name, 195 HomeDir: dir, 196 } 197 return u, nil 198 } 199 200 func current() (*User, error) { 201 t, e := syscall.OpenCurrentProcessToken() 202 if e != nil { 203 return nil, e 204 } 205 defer t.Close() 206 u, e := t.GetTokenUser() 207 if e != nil { 208 return nil, e 209 } 210 pg, e := t.GetTokenPrimaryGroup() 211 if e != nil { 212 return nil, e 213 } 214 uid, e := u.User.Sid.String() 215 if e != nil { 216 return nil, e 217 } 218 gid, e := pg.PrimaryGroup.String() 219 if e != nil { 220 return nil, e 221 } 222 dir, e := t.GetUserProfileDirectory() 223 if e != nil { 224 return nil, e 225 } 226 username, domain, e := lookupUsernameAndDomain(u.User.Sid) 227 if e != nil { 228 return nil, e 229 } 230 return newUser(uid, gid, dir, username, domain) 231 } 232 233 // lookupUserPrimaryGroup obtains the primary group SID for a user using this method: 234 // https://support.microsoft.com/en-us/help/297951/how-to-use-the-primarygroupid-attribute-to-find-the-primary-group-for 235 // The method follows this formula: domainRID + "-" + primaryGroupRID 236 func lookupUserPrimaryGroup(username, domain string) (string, error) { 237 // get the domain RID 238 sid, _, t, e := syscall.LookupSID("", domain) 239 if e != nil { 240 return "", e 241 } 242 if t != syscall.SidTypeDomain { 243 return "", fmt.Errorf("lookupUserPrimaryGroup: should be domain account type, not %d", t) 244 } 245 domainRID, e := sid.String() 246 if e != nil { 247 return "", e 248 } 249 // If the user has joined a domain use the RID of the default primary group 250 // called "Domain Users": 251 // https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems 252 // SID: S-1-5-21domain-513 253 // 254 // The correct way to obtain the primary group of a domain user is 255 // probing the user primaryGroupID attribute in the server Active Directory: 256 // https://msdn.microsoft.com/en-us/library/ms679375(v=vs.85).aspx 257 // 258 // Note that the primary group of domain users should not be modified 259 // on Windows for performance reasons, even if it's possible to do that. 260 // The .NET Developer's Guide to Directory Services Programming - Page 409 261 // https://books.google.bg/books?id=kGApqjobEfsC&lpg=PA410&ots=p7oo-eOQL7&dq=primary%20group%20RID&hl=bg&pg=PA409#v=onepage&q&f=false 262 joined, err := isDomainJoined() 263 if err == nil && joined { 264 return domainRID + "-513", nil 265 } 266 // For non-domain users call NetUserGetInfo() with level 4, which 267 // in this case would not have any network overhead. 268 // The primary group should not change from RID 513 here either 269 // but the group will be called "None" instead: 270 // https://www.adampalmer.me/iodigitalsec/2013/08/10/windows-null-session-enumeration/ 271 // "Group 'None' (RID: 513)" 272 u, e := syscall.UTF16PtrFromString(username) 273 if e != nil { 274 return "", e 275 } 276 d, e := syscall.UTF16PtrFromString(domain) 277 if e != nil { 278 return "", e 279 } 280 var p *byte 281 e = syscall.NetUserGetInfo(d, u, 4, &p) 282 if e != nil { 283 return "", e 284 } 285 defer syscall.NetApiBufferFree(p) 286 i := (*windows.UserInfo4)(unsafe.Pointer(p)) 287 return fmt.Sprintf("%s-%d", domainRID, i.PrimaryGroupID), nil 288 } 289 290 func newUserFromSid(usid *syscall.SID) (*User, error) { 291 username, domain, e := lookupUsernameAndDomain(usid) 292 if e != nil { 293 return nil, e 294 } 295 gid, e := lookupUserPrimaryGroup(username, domain) 296 if e != nil { 297 return nil, e 298 } 299 uid, e := usid.String() 300 if e != nil { 301 return nil, e 302 } 303 // If this user has logged in at least once their home path should be stored 304 // in the registry under the specified SID. References: 305 // https://social.technet.microsoft.com/wiki/contents/articles/13895.how-to-remove-a-corrupted-user-profile-from-the-registry.aspx 306 // https://support.asperasoft.com/hc/en-us/articles/216127438-How-to-delete-Windows-user-profiles 307 // 308 // The registry is the most reliable way to find the home path as the user 309 // might have decided to move it outside of the default location, 310 // (e.g. C:\users). Reference: 311 // https://answers.microsoft.com/en-us/windows/forum/windows_7-security/how-do-i-set-a-home-directory-outside-cusers-for-a/aed68262-1bf4-4a4d-93dc-7495193a440f 312 dir, e := findHomeDirInRegistry(uid) 313 if e != nil { 314 // If the home path does not exist in the registry, the user might 315 // have not logged in yet; fall back to using getProfilesDirectory(). 316 // Find the username based on a SID and append that to the result of 317 // getProfilesDirectory(). The domain is not relevant here. 318 dir, e = getProfilesDirectory() 319 if e != nil { 320 return nil, e 321 } 322 dir += `\` + username 323 } 324 return newUser(uid, gid, dir, username, domain) 325 } 326 327 func lookupUser(username string) (*User, error) { 328 sid, _, t, e := syscall.LookupSID("", username) 329 if e != nil { 330 return nil, e 331 } 332 if t != syscall.SidTypeUser { 333 return nil, fmt.Errorf("user: should be user account type, not %d", t) 334 } 335 return newUserFromSid(sid) 336 } 337 338 func lookupUserId(uid string) (*User, error) { 339 sid, e := syscall.StringToSid(uid) 340 if e != nil { 341 return nil, e 342 } 343 return newUserFromSid(sid) 344 } 345 346 func lookupGroup(groupname string) (*Group, error) { 347 sid, err := lookupGroupName(groupname) 348 if err != nil { 349 return nil, err 350 } 351 return &Group{Name: groupname, Gid: sid}, nil 352 } 353 354 func lookupGroupId(gid string) (*Group, error) { 355 sid, err := syscall.StringToSid(gid) 356 if err != nil { 357 return nil, err 358 } 359 groupname, _, t, err := sid.LookupAccount("") 360 if err != nil { 361 return nil, err 362 } 363 if t != syscall.SidTypeGroup && t != syscall.SidTypeWellKnownGroup && t != syscall.SidTypeAlias { 364 return nil, fmt.Errorf("lookupGroupId: should be group account type, not %d", t) 365 } 366 return &Group{Name: groupname, Gid: gid}, nil 367 } 368 369 func listGroups(user *User) ([]string, error) { 370 sid, err := syscall.StringToSid(user.Uid) 371 if err != nil { 372 return nil, err 373 } 374 username, domain, err := lookupUsernameAndDomain(sid) 375 if err != nil { 376 return nil, err 377 } 378 sids, err := listGroupsForUsernameAndDomain(username, domain) 379 if err != nil { 380 return nil, err 381 } 382 // Add the primary group of the user to the list if it is not already there. 383 // This is done only to comply with the POSIX concept of a primary group. 384 for _, sid := range sids { 385 if sid == user.Gid { 386 return sids, nil 387 } 388 } 389 return append(sids, user.Gid), nil 390 }