1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2009 Ted Percival
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <sys/types.h>
25 #include <errno.h>
26
27 #ifdef HAVE_PWD_H
28 #include <pwd.h>
29 #endif
30
31 #ifdef HAVE_GRP_H
32 #include <grp.h>
33 #endif
34
35 #include <pulse/xmalloc.h>
36 #include <pulsecore/macro.h>
37
38 #include "usergroup.h"
39
40 #ifdef HAVE_GRP_H
41
42 /* Returns a suitable starting size for a getgrnam_r() or getgrgid_r() buffer,
43 plus the size of a struct group.
44 */
starting_getgr_buflen(void)45 static size_t starting_getgr_buflen(void) {
46 size_t full_size;
47 long n;
48 #ifdef _SC_GETGR_R_SIZE_MAX
49 n = sysconf(_SC_GETGR_R_SIZE_MAX);
50 #else
51 n = -1;
52 #endif
53 if (n <= 0)
54 n = 512;
55
56 full_size = (size_t) n + sizeof(struct group);
57
58 if (full_size < (size_t) n) /* check for integer overflow */
59 return (size_t) n;
60
61 return full_size;
62 }
63
64 /* Returns a suitable starting size for a getpwnam_r() or getpwuid_r() buffer,
65 plus the size of a struct passwd.
66 */
starting_getpw_buflen(void)67 static size_t starting_getpw_buflen(void) {
68 long n;
69 size_t full_size;
70
71 #ifdef _SC_GETPW_R_SIZE_MAX
72 n = sysconf(_SC_GETPW_R_SIZE_MAX);
73 #else
74 n = -1;
75 #endif
76 if (n <= 0)
77 n = 512;
78
79 full_size = (size_t) n + sizeof(struct passwd);
80
81 if (full_size < (size_t) n) /* check for integer overflow */
82 return (size_t) n;
83
84 return full_size;
85 }
86
87 /* Given a memory allocation (*bufptr) and its length (*buflenptr),
88 double the size of the allocation, updating the given buffer and length
89 arguments. This function should be used in conjunction with the pa_*alloc
90 and pa_xfree functions.
91
92 Unlike realloc(), this function does *not* retain the original buffer's
93 contents.
94
95 Returns 0 on success, nonzero on error. The error cause is indicated by
96 errno.
97 */
expand_buffer_trashcontents(void ** bufptr,size_t * buflenptr)98 static int expand_buffer_trashcontents(void **bufptr, size_t *buflenptr) {
99 size_t newlen;
100
101 if (!bufptr || !*bufptr || !buflenptr) {
102 errno = EINVAL;
103 return -1;
104 }
105
106 newlen = *buflenptr * 2;
107
108 if (newlen < *buflenptr) {
109 errno = EOVERFLOW;
110 return -1;
111 }
112
113 /* Don't bother retaining memory contents; free & alloc anew */
114 pa_xfree(*bufptr);
115
116 *bufptr = pa_xmalloc(newlen);
117 *buflenptr = newlen;
118
119 return 0;
120 }
121
122 #ifdef HAVE_GETGRGID_R
123 /* Thread-safe getgrgid() replacement.
124 Returned value should be freed using pa_getgrgid_free() when the caller is
125 finished with the returned group data.
126
127 API is the same as getgrgid(), errors are indicated by a NULL return;
128 consult errno for the error cause (zero it before calling).
129 */
pa_getgrgid_malloc(gid_t gid)130 struct group *pa_getgrgid_malloc(gid_t gid) {
131 size_t buflen, getgr_buflen;
132 int err;
133 void *buf;
134 void *getgr_buf;
135 struct group *result = NULL;
136
137 buflen = starting_getgr_buflen();
138 buf = pa_xmalloc(buflen);
139
140 getgr_buflen = buflen - sizeof(struct group);
141 getgr_buf = (char *)buf + sizeof(struct group);
142
143 while ((err = getgrgid_r(gid, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) {
144 if (expand_buffer_trashcontents(&buf, &buflen))
145 break;
146
147 getgr_buflen = buflen - sizeof(struct group);
148 getgr_buf = (char *)buf + sizeof(struct group);
149 }
150
151 if (err || !result) {
152 result = NULL;
153 if (buf) {
154 pa_xfree(buf);
155 buf = NULL;
156 }
157 }
158
159 pa_assert(result == buf || result == NULL);
160
161 return result;
162 }
163
pa_getgrgid_free(struct group * grp)164 void pa_getgrgid_free(struct group *grp) {
165 pa_xfree(grp);
166 }
167
168 #else /* !HAVE_GETGRGID_R */
169
pa_getgrgid_malloc(gid_t gid)170 struct group *pa_getgrgid_malloc(gid_t gid) {
171 return getgrgid(gid);
172 }
173
pa_getgrgid_free(struct group * grp)174 void pa_getgrgid_free(struct group *grp) {
175 /* nothing */
176 return;
177 }
178
179 #endif /* !HAVE_GETGRGID_R */
180
181 #ifdef HAVE_GETGRNAM_R
182 /* Thread-safe getgrnam() function.
183 Returned value should be freed using pa_getgrnam_free() when the caller is
184 finished with the returned group data.
185
186 API is the same as getgrnam(), errors are indicated by a NULL return;
187 consult errno for the error cause (zero it before calling).
188 */
pa_getgrnam_malloc(const char * name)189 struct group *pa_getgrnam_malloc(const char *name) {
190 size_t buflen, getgr_buflen;
191 int err;
192 void *buf;
193 void *getgr_buf;
194 struct group *result = NULL;
195
196 buflen = starting_getgr_buflen();
197 buf = pa_xmalloc(buflen);
198
199 getgr_buflen = buflen - sizeof(struct group);
200 getgr_buf = (char *)buf + sizeof(struct group);
201
202 while ((err = getgrnam_r(name, (struct group *)buf, getgr_buf, getgr_buflen, &result)) == ERANGE) {
203 if (expand_buffer_trashcontents(&buf, &buflen))
204 break;
205
206 getgr_buflen = buflen - sizeof(struct group);
207 getgr_buf = (char *)buf + sizeof(struct group);
208 }
209
210 if (err || !result) {
211 result = NULL;
212 if (buf) {
213 pa_xfree(buf);
214 buf = NULL;
215 }
216 }
217
218 pa_assert(result == buf || result == NULL);
219
220 return result;
221 }
222
pa_getgrnam_free(struct group * group)223 void pa_getgrnam_free(struct group *group) {
224 pa_xfree(group);
225 }
226
227 #else /* !HAVE_GETGRNAM_R */
228
pa_getgrnam_malloc(const char * name)229 struct group *pa_getgrnam_malloc(const char *name) {
230 return getgrnam(name);
231 }
232
pa_getgrnam_free(struct group * group)233 void pa_getgrnam_free(struct group *group) {
234 /* nothing */
235 return;
236 }
237
238 #endif /* HAVE_GETGRNAM_R */
239
240 #endif /* HAVE_GRP_H */
241
242 #ifdef HAVE_PWD_H
243
244 #ifdef HAVE_GETPWNAM_R
245 /* Thread-safe getpwnam() function.
246 Returned value should be freed using pa_getpwnam_free() when the caller is
247 finished with the returned passwd data.
248
249 API is the same as getpwnam(), errors are indicated by a NULL return;
250 consult errno for the error cause (zero it before calling).
251 */
pa_getpwnam_malloc(const char * name)252 struct passwd *pa_getpwnam_malloc(const char *name) {
253 size_t buflen, getpw_buflen;
254 int err;
255 void *buf;
256 void *getpw_buf;
257 struct passwd *result = NULL;
258
259 buflen = starting_getpw_buflen();
260 buf = pa_xmalloc(buflen);
261
262 getpw_buflen = buflen - sizeof(struct passwd);
263 getpw_buf = (char *)buf + sizeof(struct passwd);
264
265 while ((err = getpwnam_r(name, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) {
266 if (expand_buffer_trashcontents(&buf, &buflen))
267 break;
268
269 getpw_buflen = buflen - sizeof(struct passwd);
270 getpw_buf = (char *)buf + sizeof(struct passwd);
271 }
272
273 if (err || !result) {
274 result = NULL;
275 if (buf) {
276 pa_xfree(buf);
277 buf = NULL;
278 }
279 }
280
281 pa_assert(result == buf || result == NULL);
282
283 return result;
284 }
285
pa_getpwnam_free(struct passwd * passwd)286 void pa_getpwnam_free(struct passwd *passwd) {
287 pa_xfree(passwd);
288 }
289
290 #else /* !HAVE_GETPWNAM_R */
291
pa_getpwnam_malloc(const char * name)292 struct passwd *pa_getpwnam_malloc(const char *name) {
293 return getpwnam(name);
294 }
295
pa_getpwnam_free(struct passwd * passwd)296 void pa_getpwnam_free(struct passwd *passwd) {
297 /* nothing */
298 return;
299 }
300
301 #endif /* !HAVE_GETPWNAM_R */
302
303 #ifdef HAVE_GETPWUID_R
304 /* Thread-safe getpwuid() function.
305 Returned value should be freed using pa_getpwuid_free() when the caller is
306 finished with the returned group data.
307
308 API is the same as getpwuid(), errors are indicated by a NULL return;
309 consult errno for the error cause (zero it before calling).
310 */
pa_getpwuid_malloc(uid_t uid)311 struct passwd *pa_getpwuid_malloc(uid_t uid) {
312 size_t buflen, getpw_buflen;
313 int err;
314 void *buf;
315 void *getpw_buf;
316 struct passwd *result = NULL;
317
318 buflen = starting_getpw_buflen();
319 buf = pa_xmalloc(buflen);
320
321 getpw_buflen = buflen - sizeof(struct passwd);
322 getpw_buf = (char *)buf + sizeof(struct passwd);
323
324 while ((err = getpwuid_r(uid, (struct passwd *)buf, getpw_buf, getpw_buflen, &result)) == ERANGE) {
325 if (expand_buffer_trashcontents(&buf, &buflen))
326 break;
327
328 getpw_buflen = buflen - sizeof(struct passwd);
329 getpw_buf = (char *)buf + sizeof(struct passwd);
330 }
331
332 if (err || !result) {
333 result = NULL;
334 if (buf) {
335 pa_xfree(buf);
336 buf = NULL;
337 }
338 }
339
340 pa_assert(result == buf || result == NULL);
341
342 return result;
343 }
344
pa_getpwuid_free(struct passwd * passwd)345 void pa_getpwuid_free(struct passwd *passwd) {
346 pa_xfree(passwd);
347 }
348
349 #else /* !HAVE_GETPWUID_R */
350
pa_getpwuid_malloc(uid_t uid)351 struct passwd *pa_getpwuid_malloc(uid_t uid) {
352 return getpwuid(uid);
353 }
354
pa_getpwuid_free(struct passwd * passwd)355 void pa_getpwuid_free(struct passwd *passwd) {
356 /* nothing */
357 return;
358 }
359
360 #endif /* !HAVE_GETPWUID_R */
361
362 #endif /* HAVE_PWD_H */
363