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 */
45static 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 */
67static 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 */
98static 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 */
130struct 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
164void pa_getgrgid_free(struct group *grp) {
165    pa_xfree(grp);
166}
167
168#else /* !HAVE_GETGRGID_R */
169
170struct group *pa_getgrgid_malloc(gid_t gid) {
171    return getgrgid(gid);
172}
173
174void 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 */
189struct 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
223void pa_getgrnam_free(struct group *group) {
224    pa_xfree(group);
225}
226
227#else /* !HAVE_GETGRNAM_R */
228
229struct group *pa_getgrnam_malloc(const char *name) {
230    return getgrnam(name);
231}
232
233void 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 */
252struct 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
286void pa_getpwnam_free(struct passwd *passwd) {
287    pa_xfree(passwd);
288}
289
290#else /* !HAVE_GETPWNAM_R */
291
292struct passwd *pa_getpwnam_malloc(const char *name) {
293    return getpwnam(name);
294}
295
296void 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 */
311struct 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
345void pa_getpwuid_free(struct passwd *passwd) {
346    pa_xfree(passwd);
347}
348
349#else /* !HAVE_GETPWUID_R */
350
351struct passwd *pa_getpwuid_malloc(uid_t uid) {
352    return getpwuid(uid);
353}
354
355void pa_getpwuid_free(struct passwd *passwd) {
356    /* nothing */
357    return;
358}
359
360#endif /* !HAVE_GETPWUID_R */
361
362#endif /* HAVE_PWD_H */
363