LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // +build darwin freebsd linux netbsd openbsd | 5 // +build darwin freebsd linux netbsd openbsd |
6 // +build cgo | 6 // +build cgo |
7 | 7 |
8 package user | 8 package user |
9 | 9 |
10 import ( | 10 import ( |
11 "fmt" | 11 "fmt" |
12 "runtime" | 12 "runtime" |
13 "strconv" | 13 "strconv" |
14 "strings" | 14 "strings" |
15 "syscall" | 15 "syscall" |
16 "unsafe" | 16 "unsafe" |
17 ) | 17 ) |
18 | 18 |
19 /* | 19 /* |
20 #include <unistd.h> | 20 #include <unistd.h> |
21 #include <sys/types.h> | 21 #include <sys/types.h> |
22 #include <pwd.h> | 22 #include <pwd.h> |
| 23 #include <grp.h> |
23 #include <stdlib.h> | 24 #include <stdlib.h> |
24 | 25 |
25 static int mygetpwuid_r(int uid, struct passwd *pwd, | 26 static int mygetpwuid_r(int uid, struct passwd *pwd, |
26 char *buf, size_t buflen, struct passwd **result) { | 27 char *buf, size_t buflen, struct passwd **result) { |
27 return getpwuid_r(uid, pwd, buf, buflen, result); | 28 return getpwuid_r(uid, pwd, buf, buflen, result); |
28 } | 29 } |
| 30 |
| 31 static int mygetgrgid_r(int gid, struct group *grp, |
| 32 char *buf, size_t buflen, struct group **result) { |
| 33 return getgrgid_r(gid, grp, buf, buflen, result); |
| 34 } |
| 35 |
| 36 static int mygetgrouplist(const char *user, gid_t group, gid_t *groups, |
| 37 int *ngroups) { |
| 38 return getgrouplist(user, group, (gid_t *)groups, ngroups); |
| 39 } |
| 40 |
| 41 static inline gid_t group_at(int i, gid_t *groups) { |
| 42 return groups[i]; |
| 43 } |
| 44 static inline char **next_member(char **members) { return members + 1; } |
29 */ | 45 */ |
30 import "C" | 46 import "C" |
31 | 47 |
| 48 const ( |
| 49 userBuffer = iota |
| 50 groupBuffer |
| 51 ) |
| 52 |
32 func current() (*User, error) { | 53 func current() (*User, error) { |
33 » return lookupUnix(syscall.Getuid(), "", false) | 54 » return lookupUnixUser(syscall.Getuid(), "", false) |
34 } | 55 } |
35 | 56 |
36 func lookup(username string) (*User, error) { | 57 func lookupUser(username string) (*User, error) { |
37 » return lookupUnix(-1, username, true) | 58 » return lookupUnixUser(-1, username, true) |
38 } | 59 } |
39 | 60 |
40 func lookupId(uid string) (*User, error) { | 61 func lookupUserId(uid string) (*User, error) { |
41 i, e := strconv.Atoi(uid) | 62 i, e := strconv.Atoi(uid) |
42 if e != nil { | 63 if e != nil { |
43 return nil, e | 64 return nil, e |
44 } | 65 } |
45 » return lookupUnix(i, "", false) | 66 » return lookupUnixUser(i, "", false) |
46 } | 67 } |
47 | 68 |
48 func lookupUnix(uid int, username string, lookupByName bool) (*User, error) { | 69 func lookupUnixUser(uid int, username string, lookupByName bool) (*User, error)
{ |
49 var pwd C.struct_passwd | 70 var pwd C.struct_passwd |
50 var result *C.struct_passwd | 71 var result *C.struct_passwd |
51 | 72 |
52 » var bufSize C.long | 73 » buf, bufSize, err := allocBuffer(userBuffer) |
53 » if runtime.GOOS == "freebsd" { | 74 » if err != nil { |
54 » » // FreeBSD doesn't have _SC_GETPW_R_SIZE_MAX | 75 » » return nil, err |
55 » » // and just returns -1. So just use the same | 76 » } |
56 » » // size that Linux returns | |
57 » » bufSize = 1024 | |
58 » } else { | |
59 » » bufSize = C.sysconf(C._SC_GETPW_R_SIZE_MAX) | |
60 » » if bufSize <= 0 || bufSize > 1<<20 { | |
61 » » » return nil, fmt.Errorf("user: unreasonable _SC_GETPW_R_S
IZE_MAX of %d", bufSize) | |
62 » » } | |
63 » } | |
64 » buf := C.malloc(C.size_t(bufSize)) | |
65 defer C.free(buf) | 77 defer C.free(buf) |
| 78 |
66 var rv C.int | 79 var rv C.int |
67 if lookupByName { | 80 if lookupByName { |
68 nameC := C.CString(username) | 81 nameC := C.CString(username) |
69 defer C.free(unsafe.Pointer(nameC)) | 82 defer C.free(unsafe.Pointer(nameC)) |
70 rv = C.getpwnam_r(nameC, | 83 rv = C.getpwnam_r(nameC, |
71 &pwd, | 84 &pwd, |
72 (*C.char)(buf), | 85 (*C.char)(buf), |
73 C.size_t(bufSize), | 86 C.size_t(bufSize), |
74 &result) | 87 &result) |
75 if rv != 0 { | 88 if rv != 0 { |
(...skipping 27 matching lines...) Expand all Loading... |
103 } | 116 } |
104 // The pw_gecos field isn't quite standardized. Some docs | 117 // The pw_gecos field isn't quite standardized. Some docs |
105 // say: "It is expected to be a comma separated list of | 118 // say: "It is expected to be a comma separated list of |
106 // personal data where the first item is the full name of the | 119 // personal data where the first item is the full name of the |
107 // user." | 120 // user." |
108 if i := strings.Index(u.Name, ","); i >= 0 { | 121 if i := strings.Index(u.Name, ","); i >= 0 { |
109 u.Name = u.Name[:i] | 122 u.Name = u.Name[:i] |
110 } | 123 } |
111 return u, nil | 124 return u, nil |
112 } | 125 } |
| 126 |
| 127 func currentGroup() (*Group, error) { |
| 128 return lookupUnixGroup(syscall.Getgid(), "", false, buildGroup) |
| 129 } |
| 130 |
| 131 func lookupGroup(groupname string) (*Group, error) { |
| 132 return lookupUnixGroup(-1, groupname, true, buildGroup) |
| 133 } |
| 134 |
| 135 func lookupGroupId(gid string) (*Group, error) { |
| 136 i, e := strconv.Atoi(gid) |
| 137 if e != nil { |
| 138 return nil, e |
| 139 } |
| 140 return lookupUnixGroup(i, "", false, buildGroup) |
| 141 } |
| 142 |
| 143 func lookupUnixGroup(gid int, groupname string, lookupByName bool, f func(*C.str
uct_group) *Group) (*Group, error) { |
| 144 var grp C.struct_group |
| 145 var result *C.struct_group |
| 146 |
| 147 buf, bufSize, err := allocBuffer(groupBuffer) |
| 148 if err != nil { |
| 149 return nil, err |
| 150 } |
| 151 defer C.free(buf) |
| 152 |
| 153 if lookupByName { |
| 154 nameC := C.CString(groupname) |
| 155 defer C.free(unsafe.Pointer(nameC)) |
| 156 rv := C.getgrnam_r(nameC, |
| 157 &grp, |
| 158 (*C.char)(buf), |
| 159 C.size_t(bufSize), |
| 160 &result) |
| 161 if rv != 0 { |
| 162 return nil, fmt.Errorf("group: lookup groupname %s: %s",
groupname, syscall.Errno(rv)) |
| 163 } |
| 164 if result == nil { |
| 165 return nil, UnknownGroupError(groupname) |
| 166 } |
| 167 } else { |
| 168 // mygetgrgid_r is a wrapper around getgrgid_r to |
| 169 // to avoid using gid_t because C.gid_t(gid) for |
| 170 // unknown reasons doesn't work on linux. |
| 171 rv := C.mygetgrgid_r(C.int(gid), |
| 172 &grp, |
| 173 (*C.char)(buf), |
| 174 C.size_t(bufSize), |
| 175 &result) |
| 176 if rv != 0 { |
| 177 return nil, fmt.Errorf("group: lookup groupid %d: %s", g
id, syscall.Errno(rv)) |
| 178 } |
| 179 if result == nil { |
| 180 return nil, UnknownGroupIdError(gid) |
| 181 } |
| 182 } |
| 183 g := f(&grp) |
| 184 return g, nil |
| 185 } |
| 186 |
| 187 func buildGroup(grp *C.struct_group) *Group { |
| 188 g := &Group{ |
| 189 Gid: strconv.Itoa(int(grp.gr_gid)), |
| 190 Name: C.GoString(grp.gr_name), |
| 191 } |
| 192 return g |
| 193 } |
| 194 |
| 195 func userInGroup(u *User, g *Group) (bool, error) { |
| 196 if u.Gid == g.Gid { |
| 197 return true, nil |
| 198 } |
| 199 gid, err := strconv.Atoi(g.Gid) |
| 200 if err != nil { |
| 201 return false, err |
| 202 } |
| 203 |
| 204 nameC := C.CString(u.Username) |
| 205 defer C.free(unsafe.Pointer(nameC)) |
| 206 groupC := C.gid_t(gid) |
| 207 ngroupsC := C.int(0) |
| 208 |
| 209 C.mygetgrouplist(nameC, groupC, nil, &ngroupsC) |
| 210 ngroups := int(ngroupsC) |
| 211 |
| 212 groups := C.malloc(C.size_t(int(unsafe.Sizeof(groupC)) * ngroups)) |
| 213 defer C.free(groups) |
| 214 |
| 215 rv := C.mygetgrouplist(nameC, groupC, (*C.gid_t)(groups), &ngroupsC) |
| 216 if rv == -1 { |
| 217 return false, fmt.Errorf("user: membership of %s in %s: %s", u.U
sername, g.Name, syscall.Errno(rv)) |
| 218 } |
| 219 |
| 220 ngroups = int(ngroupsC) |
| 221 for i := 0; i < ngroups; i++ { |
| 222 gid := C.group_at(C.int(i), (*C.gid_t)(groups)) |
| 223 if g.Gid == strconv.Itoa(int(gid)) { |
| 224 return true, nil |
| 225 } |
| 226 } |
| 227 return false, nil |
| 228 } |
| 229 |
| 230 func groupMembers(g *Group) ([]string, error) { |
| 231 var members []string |
| 232 gid, err := strconv.Atoi(g.Gid) |
| 233 if err != nil { |
| 234 return nil, err |
| 235 } |
| 236 |
| 237 _, err = lookupUnixGroup(gid, "", false, func(grp *C.struct_group) *Grou
p { |
| 238 cmem := grp.gr_mem |
| 239 for *cmem != nil { |
| 240 members = append(members, C.GoString(*cmem)) |
| 241 cmem = C.next_member(cmem) |
| 242 } |
| 243 return g |
| 244 }) |
| 245 if err != nil { |
| 246 return nil, err |
| 247 } |
| 248 |
| 249 return members, nil |
| 250 } |
| 251 |
| 252 func allocBuffer(bufType int) (unsafe.Pointer, C.long, error) { |
| 253 var bufSize C.long |
| 254 if runtime.GOOS == "freebsd" { |
| 255 // FreeBSD doesn't have _SC_GETPW_R_SIZE_MAX |
| 256 // or SC_GETGR_R_SIZE_MAX and just returns -1. |
| 257 // So just use the same size that Linux returns |
| 258 bufSize = 1024 |
| 259 } else { |
| 260 var size C.int |
| 261 var constName string |
| 262 switch bufType { |
| 263 case userBuffer: |
| 264 size = C._SC_GETPW_R_SIZE_MAX |
| 265 constName = "_SC_GETPW_R_SIZE_MAX" |
| 266 case groupBuffer: |
| 267 size = C._SC_GETGR_R_SIZE_MAX |
| 268 constName = "_SC_GETGR_R_SIZE_MAX" |
| 269 } |
| 270 bufSize = C.sysconf(size) |
| 271 if bufSize <= 0 || bufSize > 1<<20 { |
| 272 return nil, bufSize, fmt.Errorf("user: unreasonable %s o
f %d", constName, bufSize) |
| 273 } |
| 274 } |
| 275 return C.malloc(C.size_t(bufSize)), bufSize, nil |
| 276 } |
LEFT | RIGHT |