blob: 58ecf324057a9817adf92753ab667df16f37d380 [file] [log] [blame]
Dan Willemsend2797482017-07-26 13:13:13 -07001// Copyright 2011 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// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris
6// +build cgo
7
8package user
9
10import (
11 "fmt"
12 "strconv"
13 "strings"
14 "syscall"
15 "unsafe"
16)
17
18/*
19#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS
20#include <unistd.h>
21#include <sys/types.h>
22#include <pwd.h>
23#include <grp.h>
24#include <stdlib.h>
25
26static int mygetpwuid_r(int uid, struct passwd *pwd,
27 char *buf, size_t buflen, struct passwd **result) {
28 return getpwuid_r(uid, pwd, buf, buflen, result);
29}
30
31static int mygetpwnam_r(const char *name, struct passwd *pwd,
32 char *buf, size_t buflen, struct passwd **result) {
33 return getpwnam_r(name, pwd, buf, buflen, result);
34}
35
36static int mygetgrgid_r(int gid, struct group *grp,
37 char *buf, size_t buflen, struct group **result) {
38 return getgrgid_r(gid, grp, buf, buflen, result);
39}
40
41static int mygetgrnam_r(const char *name, struct group *grp,
42 char *buf, size_t buflen, struct group **result) {
43 return getgrnam_r(name, grp, buf, buflen, result);
44}
45*/
46import "C"
47
48func current() (*User, error) {
49 return lookupUnixUid(syscall.Getuid())
50}
51
52func lookupUser(username string) (*User, error) {
53 var pwd C.struct_passwd
54 var result *C.struct_passwd
55 nameC := C.CString(username)
56 defer C.free(unsafe.Pointer(nameC))
57
58 buf := alloc(userBuffer)
59 defer buf.free()
60
61 err := retryWithBuffer(buf, func() syscall.Errno {
62 // mygetpwnam_r is a wrapper around getpwnam_r to avoid
63 // passing a size_t to getpwnam_r, because for unknown
64 // reasons passing a size_t to getpwnam_r doesn't work on
65 // Solaris.
66 return syscall.Errno(C.mygetpwnam_r(nameC,
67 &pwd,
68 (*C.char)(buf.ptr),
69 C.size_t(buf.size),
70 &result))
71 })
72 if err != nil {
73 return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
74 }
75 if result == nil {
76 return nil, UnknownUserError(username)
77 }
78 return buildUser(&pwd), err
79}
80
81func lookupUserId(uid string) (*User, error) {
82 i, e := strconv.Atoi(uid)
83 if e != nil {
84 return nil, e
85 }
86 return lookupUnixUid(i)
87}
88
89func lookupUnixUid(uid int) (*User, error) {
90 var pwd C.struct_passwd
91 var result *C.struct_passwd
92
93 buf := alloc(userBuffer)
94 defer buf.free()
95
96 err := retryWithBuffer(buf, func() syscall.Errno {
97 // mygetpwuid_r is a wrapper around getpwuid_r to
98 // to avoid using uid_t because C.uid_t(uid) for
99 // unknown reasons doesn't work on linux.
100 return syscall.Errno(C.mygetpwuid_r(C.int(uid),
101 &pwd,
102 (*C.char)(buf.ptr),
103 C.size_t(buf.size),
104 &result))
105 })
106 if err != nil {
107 return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
108 }
109 if result == nil {
110 return nil, UnknownUserIdError(uid)
111 }
112 return buildUser(&pwd), nil
113}
114
115func buildUser(pwd *C.struct_passwd) *User {
116 u := &User{
117 Uid: strconv.Itoa(int(pwd.pw_uid)),
118 Gid: strconv.Itoa(int(pwd.pw_gid)),
119 Username: C.GoString(pwd.pw_name),
120 Name: C.GoString(pwd.pw_gecos),
121 HomeDir: C.GoString(pwd.pw_dir),
122 }
123 // The pw_gecos field isn't quite standardized. Some docs
124 // say: "It is expected to be a comma separated list of
125 // personal data where the first item is the full name of the
126 // user."
127 if i := strings.Index(u.Name, ","); i >= 0 {
128 u.Name = u.Name[:i]
129 }
130 return u
131}
132
133func currentGroup() (*Group, error) {
134 return lookupUnixGid(syscall.Getgid())
135}
136
137func lookupGroup(groupname string) (*Group, error) {
138 var grp C.struct_group
139 var result *C.struct_group
140
141 buf := alloc(groupBuffer)
142 defer buf.free()
143 cname := C.CString(groupname)
144 defer C.free(unsafe.Pointer(cname))
145
146 err := retryWithBuffer(buf, func() syscall.Errno {
147 return syscall.Errno(C.mygetgrnam_r(cname,
148 &grp,
149 (*C.char)(buf.ptr),
150 C.size_t(buf.size),
151 &result))
152 })
153 if err != nil {
154 return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
155 }
156 if result == nil {
157 return nil, UnknownGroupError(groupname)
158 }
159 return buildGroup(&grp), nil
160}
161
162func lookupGroupId(gid string) (*Group, error) {
163 i, e := strconv.Atoi(gid)
164 if e != nil {
165 return nil, e
166 }
167 return lookupUnixGid(i)
168}
169
170func lookupUnixGid(gid int) (*Group, error) {
171 var grp C.struct_group
172 var result *C.struct_group
173
174 buf := alloc(groupBuffer)
175 defer buf.free()
176
177 err := retryWithBuffer(buf, func() syscall.Errno {
178 // mygetgrgid_r is a wrapper around getgrgid_r to
179 // to avoid using gid_t because C.gid_t(gid) for
180 // unknown reasons doesn't work on linux.
181 return syscall.Errno(C.mygetgrgid_r(C.int(gid),
182 &grp,
183 (*C.char)(buf.ptr),
184 C.size_t(buf.size),
185 &result))
186 })
187 if err != nil {
188 return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
189 }
190 if result == nil {
191 return nil, UnknownGroupIdError(strconv.Itoa(gid))
192 }
193 return buildGroup(&grp), nil
194}
195
196func buildGroup(grp *C.struct_group) *Group {
197 g := &Group{
198 Gid: strconv.Itoa(int(grp.gr_gid)),
199 Name: C.GoString(grp.gr_name),
200 }
201 return g
202}
203
204type bufferKind C.int
205
206const (
207 userBuffer = bufferKind(C._SC_GETPW_R_SIZE_MAX)
208 groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX)
209)
210
211func (k bufferKind) initialSize() C.size_t {
212 sz := C.sysconf(C.int(k))
213 if sz == -1 {
214 // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
215 // Additionally, not all Linux systems have it, either. For
216 // example, the musl libc returns -1.
217 return 1024
218 }
219 if !isSizeReasonable(int64(sz)) {
220 // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run.
221 return maxBufferSize
222 }
223 return C.size_t(sz)
224}
225
226type memBuffer struct {
227 ptr unsafe.Pointer
228 size C.size_t
229}
230
231func alloc(kind bufferKind) *memBuffer {
232 sz := kind.initialSize()
233 return &memBuffer{
234 ptr: C.malloc(sz),
235 size: sz,
236 }
237}
238
239func (mb *memBuffer) resize(newSize C.size_t) {
240 mb.ptr = C.realloc(mb.ptr, newSize)
241 mb.size = newSize
242}
243
244func (mb *memBuffer) free() {
245 C.free(mb.ptr)
246}
247
248// retryWithBuffer repeatedly calls f(), increasing the size of the
249// buffer each time, until f succeeds, fails with a non-ERANGE error,
250// or the buffer exceeds a reasonable limit.
251func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
252 for {
253 errno := f()
254 if errno == 0 {
255 return nil
256 } else if errno != syscall.ERANGE {
257 return errno
258 }
259 newSize := buf.size * 2
260 if !isSizeReasonable(int64(newSize)) {
261 return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
262 }
263 buf.resize(newSize)
264 }
265}
266
267const maxBufferSize = 1 << 20
268
269func isSizeReasonable(sz int64) bool {
270 return sz > 0 && sz <= maxBufferSize
271}