github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/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 "errors" 9 "fmt" 10 "internal/syscall/windows" 11 "internal/syscall/windows/registry" 12 "syscall" 13 "unsafe" 14 ) 15 16 func init() { 17 groupImplemented = false 18 } 19 20 func isDomainJoined() (bool, error) { 21 var domain *uint16 22 var status uint32 23 err := syscall.NetGetJoinInformation(nil, &domain, &status) 24 if err != nil { 25 return false, err 26 } 27 syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain))) 28 return status == syscall.NetSetupDomainName, nil 29 } 30 31 func lookupFullNameDomain(domainAndUser string) (string, error) { 32 return syscall.TranslateAccountName(domainAndUser, 33 syscall.NameSamCompatible, syscall.NameDisplay, 50) 34 } 35 36 func lookupFullNameServer(servername, username string) (string, error) { 37 s, e := syscall.UTF16PtrFromString(servername) 38 if e != nil { 39 return "", e 40 } 41 u, e := syscall.UTF16PtrFromString(username) 42 if e != nil { 43 return "", e 44 } 45 var p *byte 46 e = syscall.NetUserGetInfo(s, u, 10, &p) 47 if e != nil { 48 return "", e 49 } 50 defer syscall.NetApiBufferFree(p) 51 i := (*syscall.UserInfo10)(unsafe.Pointer(p)) 52 if i.FullName == nil { 53 return "", nil 54 } 55 name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:]) 56 return name, nil 57 } 58 59 func lookupFullName(domain, username, domainAndUser string) (string, error) { 60 joined, err := isDomainJoined() 61 if err == nil && joined { 62 name, err := lookupFullNameDomain(domainAndUser) 63 if err == nil { 64 return name, nil 65 } 66 } 67 name, err := lookupFullNameServer(domain, username) 68 if err == nil { 69 return name, nil 70 } 71 // domain worked neither as a domain nor as a server 72 // could be domain server unavailable 73 // pretend username is fullname 74 return username, nil 75 } 76 77 // getProfilesDirectory retrieves the path to the root directory 78 // where user profiles are stored. 79 func getProfilesDirectory() (string, error) { 80 n := uint32(100) 81 for { 82 b := make([]uint16, n) 83 e := windows.GetProfilesDirectory(&b[0], &n) 84 if e == nil { 85 return syscall.UTF16ToString(b), nil 86 } 87 if e != syscall.ERROR_INSUFFICIENT_BUFFER { 88 return "", e 89 } 90 if n <= uint32(len(b)) { 91 return "", e 92 } 93 } 94 } 95 96 // lookupUsernameAndDomain obtains username and domain for usid. 97 func lookupUsernameAndDomain(usid *syscall.SID) (username, domain string, e error) { 98 username, domain, t, e := usid.LookupAccount("") 99 if e != nil { 100 return "", "", e 101 } 102 if t != syscall.SidTypeUser { 103 return "", "", fmt.Errorf("user: should be user account type, not %d", t) 104 } 105 return username, domain, nil 106 } 107 108 // findHomeDirInRegistry finds the user home path based on usid string 109 func findHomeDirInRegistry(uid string) (dir string, e error) { 110 k, e := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\`+uid, registry.QUERY_VALUE) 111 if e != nil { 112 return "", e 113 } 114 defer k.Close() 115 dir, _, e = k.GetStringValue("ProfileImagePath") 116 if e != nil { 117 return "", e 118 } 119 return dir, nil 120 } 121 122 func newUser(uid, gid, dir, username, domain string) (*User, error) { 123 domainAndUser := domain + `\` + username 124 name, e := lookupFullName(domain, username, domainAndUser) 125 if e != nil { 126 return nil, e 127 } 128 u := &User{ 129 Uid: uid, 130 Gid: gid, 131 Username: domainAndUser, 132 Name: name, 133 HomeDir: dir, 134 } 135 return u, nil 136 } 137 138 func current() (*User, error) { 139 t, e := syscall.OpenCurrentProcessToken() 140 if e != nil { 141 return nil, e 142 } 143 defer t.Close() 144 u, e := t.GetTokenUser() 145 if e != nil { 146 return nil, e 147 } 148 pg, e := t.GetTokenPrimaryGroup() 149 if e != nil { 150 return nil, e 151 } 152 uid, e := u.User.Sid.String() 153 if e != nil { 154 return nil, e 155 } 156 gid, e := pg.PrimaryGroup.String() 157 if e != nil { 158 return nil, e 159 } 160 dir, e := t.GetUserProfileDirectory() 161 if e != nil { 162 return nil, e 163 } 164 username, domain, e := lookupUsernameAndDomain(u.User.Sid) 165 if e != nil { 166 return nil, e 167 } 168 return newUser(uid, gid, dir, username, domain) 169 } 170 171 // TODO: The Gid field in the User struct is not set on Windows. 172 173 func newUserFromSid(usid *syscall.SID) (*User, error) { 174 gid := "unknown" 175 username, domain, e := lookupUsernameAndDomain(usid) 176 if e != nil { 177 return nil, e 178 } 179 uid, e := usid.String() 180 if e != nil { 181 return nil, e 182 } 183 // if this user has logged at least once his home path should be stored 184 // in the registry under his SID. references: 185 // https://social.technet.microsoft.com/wiki/contents/articles/13895.how-to-remove-a-corrupted-user-profile-from-the-registry.aspx 186 // https://support.asperasoft.com/hc/en-us/articles/216127438-How-to-delete-Windows-user-profiles 187 // 188 // the registry is the most reliable way to find the home path as the user 189 // might have decided to move it outside of the default location 190 // (e.g. c:\users). reference: 191 // 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 192 dir, e := findHomeDirInRegistry(uid) 193 if e != nil { 194 // if the home path does not exists in the registry, the user might have 195 // not logged in yet; fall back to using getProfilesDirectory(). find the 196 // username based on a SID and append that to the result of 197 // getProfilesDirectory(). the domain is not of relevance here. 198 dir, e = getProfilesDirectory() 199 if e != nil { 200 return nil, e 201 } 202 dir += `\` + username 203 } 204 return newUser(uid, gid, dir, username, domain) 205 } 206 207 func lookupUser(username string) (*User, error) { 208 sid, _, t, e := syscall.LookupSID("", username) 209 if e != nil { 210 return nil, e 211 } 212 if t != syscall.SidTypeUser { 213 return nil, fmt.Errorf("user: should be user account type, not %d", t) 214 } 215 return newUserFromSid(sid) 216 } 217 218 func lookupUserId(uid string) (*User, error) { 219 sid, e := syscall.StringToSid(uid) 220 if e != nil { 221 return nil, e 222 } 223 return newUserFromSid(sid) 224 } 225 226 func lookupGroup(groupname string) (*Group, error) { 227 return nil, errors.New("user: LookupGroup not implemented on windows") 228 } 229 230 func lookupGroupId(string) (*Group, error) { 231 return nil, errors.New("user: LookupGroupId not implemented on windows") 232 } 233 234 func listGroups(*User) ([]string, error) { 235 return nil, errors.New("user: GroupIds not implemented on windows") 236 }