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 "syscall" 10 "unsafe" 11 ) 12 13 func isDomainJoined() (bool, error) { 14 var domain *uint16 15 var status uint32 16 err := syscall.NetGetJoinInformation(nil, &domain, &status) 17 if err != nil { 18 return false, err 19 } 20 syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain))) 21 return status == syscall.NetSetupDomainName, nil 22 } 23 24 func lookupFullNameDomain(domainAndUser string) (string, error) { 25 return syscall.TranslateAccountName(domainAndUser, 26 syscall.NameSamCompatible, syscall.NameDisplay, 50) 27 } 28 29 func lookupFullNameServer(servername, username string) (string, error) { 30 s, e := syscall.UTF16PtrFromString(servername) 31 if e != nil { 32 return "", e 33 } 34 u, e := syscall.UTF16PtrFromString(username) 35 if e != nil { 36 return "", e 37 } 38 var p *byte 39 e = syscall.NetUserGetInfo(s, u, 10, &p) 40 if e != nil { 41 return "", e 42 } 43 defer syscall.NetApiBufferFree(p) 44 i := (*syscall.UserInfo10)(unsafe.Pointer(p)) 45 if i.FullName == nil { 46 return "", nil 47 } 48 name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:]) 49 return name, 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 // domain worked neigher as a domain nor as a server 65 // could be domain server unavailable 66 // pretend username is fullname 67 return username, nil 68 } 69 70 func newUser(usid *syscall.SID, gid, dir string) (*User, error) { 71 username, domain, t, e := usid.LookupAccount("") 72 if e != nil { 73 return nil, e 74 } 75 if t != syscall.SidTypeUser { 76 return nil, fmt.Errorf("user: should be user account type, not %d", t) 77 } 78 domainAndUser := domain + `\` + username 79 uid, e := usid.String() 80 if e != nil { 81 return nil, e 82 } 83 name, e := lookupFullName(domain, username, domainAndUser) 84 if e != nil { 85 return nil, e 86 } 87 u := &User{ 88 Uid: uid, 89 Gid: gid, 90 Username: domainAndUser, 91 Name: name, 92 HomeDir: dir, 93 } 94 return u, nil 95 } 96 97 func current() (*User, error) { 98 t, e := syscall.OpenCurrentProcessToken() 99 if e != nil { 100 return nil, e 101 } 102 defer t.Close() 103 u, e := t.GetTokenUser() 104 if e != nil { 105 return nil, e 106 } 107 pg, e := t.GetTokenPrimaryGroup() 108 if e != nil { 109 return nil, e 110 } 111 gid, e := pg.PrimaryGroup.String() 112 if e != nil { 113 return nil, e 114 } 115 dir, e := t.GetUserProfileDirectory() 116 if e != nil { 117 return nil, e 118 } 119 return newUser(u.User.Sid, gid, dir) 120 } 121 122 // BUG(brainman): Lookup and LookupId functions do not set 123 // Gid and HomeDir fields in the User struct returned on windows. 124 125 func newUserFromSid(usid *syscall.SID) (*User, error) { 126 // TODO(brainman): do not know where to get gid and dir fields 127 gid := "unknown" 128 dir := "Unknown directory" 129 return newUser(usid, gid, dir) 130 } 131 132 func lookup(username string) (*User, error) { 133 sid, _, t, e := syscall.LookupSID("", username) 134 if e != nil { 135 return nil, e 136 } 137 if t != syscall.SidTypeUser { 138 return nil, fmt.Errorf("user: should be user account type, not %d", t) 139 } 140 return newUserFromSid(sid) 141 } 142 143 func lookupId(uid string) (*User, error) { 144 sid, e := syscall.StringToSid(uid) 145 if e != nil { 146 return nil, e 147 } 148 return newUserFromSid(sid) 149 } 150