]> git.proxmox.com Git - mirror_lxc.git/blob - src/include/getgrgid_r.c
spelling: output
[mirror_lxc.git] / src / include / getgrgid_r.c
1 /* liblxcapi
2 *
3 * Copyright © 2018 Christian Brauner <christian.brauner@ubuntu.com>.
4 * Copyright © 2018 Canonical Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2, as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * This function has been copied from musl.
20 */
21
22 #define _GNU_SOURCE
23 #include <byteswap.h>
24 #include <errno.h>
25 #include <grp.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/socket.h>
31 #include <pthread.h>
32 #include <unistd.h>
33
34 #define LOGIN_NAME_MAX 256
35 #define NSCDVERSION 2
36 #define GETPWBYNAME 0
37 #define GETPWBYUID 1
38 #define GETGRBYNAME 2
39 #define GETGRBYGID 3
40 #define GETINITGR 15
41
42 #define REQVERSION 0
43 #define REQTYPE 1
44 #define REQKEYLEN 2
45 #define REQ_LEN 3
46
47 #define PWVERSION 0
48 #define PWFOUND 1
49 #define PWNAMELEN 2
50 #define PWPASSWDLEN 3
51 #define PWUID 4
52 #define PWGID 5
53 #define PWGECOSLEN 6
54 #define PWDIRLEN 7
55 #define PWSHELLLEN 8
56 #define PW_LEN 9
57
58 #define GRVERSION 0
59 #define GRFOUND 1
60 #define GRNAMELEN 2
61 #define GRPASSWDLEN 3
62 #define GRGID 4
63 #define GRMEMCNT 5
64 #define GR_LEN 6
65
66 #define INITGRVERSION 0
67 #define INITGRFOUND 1
68 #define INITGRNGRPS 2
69 #define INITGR_LEN 3
70
71 #define FIX(x) (gr->gr_##x = gr->gr_##x - line + buf)
72
73 static unsigned atou(char **s)
74 {
75 unsigned x;
76 for (x = 0; **s - '0' < 10U; ++*s)
77 x = 10 * x + (**s - '0');
78 return x;
79 }
80
81 static int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size,
82 char ***mem, size_t *nmem, struct group **res)
83 {
84 ssize_t l;
85 char *s, *mems;
86 size_t i;
87 int rv = 0;
88
89 #ifdef HAVE_PTHREAD_SETCANCELSTATE
90 int cs;
91
92 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
93 #endif
94
95 for (;;) {
96 if ((l = getline(line, size, f)) < 0) {
97 rv = ferror(f) ? errno : 0;
98 free(*line);
99 *line = 0;
100 gr = 0;
101 goto end;
102 }
103 line[0][l - 1] = 0;
104
105 s = line[0];
106 gr->gr_name = s++;
107 if (!(s = strchr(s, ':')))
108 continue;
109
110 *s++ = 0;
111 gr->gr_passwd = s;
112 if (!(s = strchr(s, ':')))
113 continue;
114
115 *s++ = 0;
116 gr->gr_gid = atou(&s);
117 if (*s != ':')
118 continue;
119
120 *s++ = 0;
121 mems = s;
122 break;
123 }
124
125 for (*nmem = !!*s; *s; s++)
126 if (*s == ',')
127 ++*nmem;
128 free(*mem);
129 *mem = calloc(sizeof(char *), *nmem + 1);
130 if (!*mem) {
131 rv = errno;
132 free(*line);
133 *line = 0;
134 gr = 0;
135 goto end;
136 }
137 if (*mems) {
138 mem[0][0] = mems;
139 for (s = mems, i = 0; *s; s++)
140 if (*s == ',')
141 *s++ = 0, mem[0][++i] = s;
142 mem[0][++i] = 0;
143 } else {
144 mem[0][0] = 0;
145 }
146 gr->gr_mem = *mem;
147 end:
148
149 #ifdef HAVE_PTHREAD_SETCANCELSTATE
150 pthread_setcancelstate(cs, 0);
151 #endif
152
153 *res = gr;
154 if (rv)
155 errno = rv;
156 return rv;
157 }
158
159 static char *itoa(char *p, uint32_t x)
160 {
161 // number of digits in a uint32_t + NUL
162 p += 11;
163 *--p = 0;
164 do {
165 *--p = '0' + x % 10;
166 x /= 10;
167 } while (x);
168 return p;
169 }
170
171 static const struct {
172 short sun_family;
173 char sun_path[21];
174 } addr = {AF_UNIX, "/var/run/nscd/socket"};
175
176 static FILE *__nscd_query(int32_t req, const char *key, int32_t *buf,
177 size_t len, int *swap)
178 {
179 size_t i;
180 int fd;
181 FILE *f = 0;
182 int32_t req_buf[REQ_LEN] = {NSCDVERSION, req,
183 strnlen(key, LOGIN_NAME_MAX) + 1};
184 struct msghdr msg = {.msg_iov =
185 (struct iovec[]){{&req_buf, sizeof(req_buf)},
186 {(char *)key, strlen(key) + 1}},
187 .msg_iovlen = 2};
188 int errno_save = errno;
189
190 *swap = 0;
191 retry:
192 memset(buf, 0, len);
193 buf[0] = NSCDVERSION;
194
195 fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
196 if (fd < 0)
197 return NULL;
198
199 if (!(f = fdopen(fd, "r"))) {
200 close(fd);
201 return 0;
202 }
203
204 if (req_buf[2] > LOGIN_NAME_MAX)
205 return f;
206
207 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
208 /* If there isn't a running nscd we simulate a "not found"
209 * result and the caller is responsible for calling
210 * fclose on the (unconnected) socket. The value of
211 * errno must be left unchanged in this case. */
212 if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT) {
213 errno = errno_save;
214 return f;
215 }
216 goto error;
217 }
218
219 if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0)
220 goto error;
221
222 if (!fread(buf, len, 1, f)) {
223 /* If the VERSION entry mismatches nscd will disconnect. The
224 * most likely cause is that the endianness mismatched. So, we
225 * byteswap and try once more. (if we already swapped, just
226 * fail out)
227 */
228 if (ferror(f))
229 goto error;
230 if (!*swap) {
231 fclose(f);
232 for (i = 0; i < sizeof(req_buf) / sizeof(req_buf[0]);
233 i++) {
234 req_buf[i] = bswap_32(req_buf[i]);
235 }
236 *swap = 1;
237 goto retry;
238 } else {
239 errno = EIO;
240 goto error;
241 }
242 }
243
244 if (*swap) {
245 for (i = 0; i < len / sizeof(buf[0]); i++) {
246 buf[i] = bswap_32(buf[i]);
247 }
248 }
249
250 /* The first entry in every nscd response is the version number. This
251 * really shouldn't happen, and is evidence of some form of malformed
252 * response.
253 */
254 if (buf[0] != NSCDVERSION) {
255 errno = EIO;
256 goto error;
257 }
258
259 return f;
260 error:
261 fclose(f);
262 return 0;
263 }
264
265 static int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf,
266 size_t *size, char ***mem, size_t *nmem, struct group **res)
267 {
268 FILE *f;
269 int rv = 0;
270 #ifdef HAVE_PTHREAD_SETCANCELSTATE
271 int cs;
272
273 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
274 #endif
275
276 *res = 0;
277
278 f = fopen("/etc/group", "rbe");
279 if (!f) {
280 rv = errno;
281 goto done;
282 }
283
284 while (!(rv = __getgrent_a(f, gr, buf, size, mem, nmem, res)) && *res) {
285 if ((name && !strcmp(name, (*res)->gr_name)) ||
286 (!name && (*res)->gr_gid == gid)) {
287 break;
288 }
289 }
290 fclose(f);
291
292 if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) {
293 int32_t req = name ? GETGRBYNAME : GETGRBYGID;
294 int32_t i;
295 const char *key;
296 int32_t groupbuf[GR_LEN] = {0};
297 size_t len = 0;
298 size_t grlist_len = 0;
299 char gidbuf[11] = {0};
300 int swap = 0;
301 char *ptr;
302
303 if (name) {
304 key = name;
305 } else {
306 if (gid < 0 || gid > UINT32_MAX) {
307 rv = 0;
308 goto done;
309 }
310 key = itoa(gidbuf, gid);
311 }
312
313 f = __nscd_query(req, key, groupbuf, sizeof groupbuf, &swap);
314 if (!f) {
315 rv = errno;
316 goto done;
317 }
318
319 if (!groupbuf[GRFOUND]) {
320 rv = 0;
321 goto cleanup_f;
322 }
323
324 if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) {
325 rv = EIO;
326 goto cleanup_f;
327 }
328
329 if ((int64_t)groupbuf[GRNAMELEN] >
330 (int64_t)(SIZE_MAX - groupbuf[GRPASSWDLEN])) {
331 rv = ENOMEM;
332 goto cleanup_f;
333 }
334 len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
335
336 for (i = 0; i < groupbuf[GRMEMCNT]; i++) {
337 uint32_t name_len;
338 if (fread(&name_len, sizeof name_len, 1, f) < 1) {
339 rv = ferror(f) ? errno : EIO;
340 goto cleanup_f;
341 }
342 if (swap) {
343 name_len = bswap_32(name_len);
344 }
345 if (name_len > SIZE_MAX - grlist_len ||
346 name_len > SIZE_MAX - len) {
347 rv = ENOMEM;
348 goto cleanup_f;
349 }
350 len += name_len;
351 grlist_len += name_len;
352 }
353
354 if (len > *size || !*buf) {
355 char *tmp = realloc(*buf, len);
356 if (!tmp) {
357 rv = errno;
358 goto cleanup_f;
359 }
360 *buf = tmp;
361 *size = len;
362 }
363
364 if (!fread(*buf, len, 1, f)) {
365 rv = ferror(f) ? errno : EIO;
366 goto cleanup_f;
367 }
368
369 if (((size_t)(groupbuf[GRMEMCNT] + 1)) > *nmem) {
370 if (((size_t)(groupbuf[GRMEMCNT] + 1)) >
371 (SIZE_MAX / sizeof(char *))) {
372 rv = ENOMEM;
373 goto cleanup_f;
374 }
375 char **tmp = realloc(*mem, (groupbuf[GRMEMCNT] + 1) *
376 sizeof(char *));
377 if (!tmp) {
378 rv = errno;
379 goto cleanup_f;
380 }
381 *mem = tmp;
382 *nmem = groupbuf[GRMEMCNT] + 1;
383 }
384
385 if (groupbuf[GRMEMCNT]) {
386 mem[0][0] =
387 *buf + groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
388 for (ptr = mem[0][0], i = 0;
389 ptr != mem[0][0] + grlist_len; ptr++)
390 if (!*ptr)
391 mem[0][++i] = ptr + 1;
392 mem[0][i] = 0;
393
394 if (i != groupbuf[GRMEMCNT]) {
395 rv = EIO;
396 goto cleanup_f;
397 }
398 } else {
399 mem[0][0] = 0;
400 }
401
402 gr->gr_name = *buf;
403 gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN];
404 gr->gr_gid = groupbuf[GRGID];
405 gr->gr_mem = *mem;
406
407 if (gr->gr_passwd[-1] ||
408 gr->gr_passwd[groupbuf[GRPASSWDLEN] - 1]) {
409 rv = EIO;
410 goto cleanup_f;
411 }
412
413 if ((name && strcmp(name, gr->gr_name)) ||
414 (!name && gid != gr->gr_gid)) {
415 rv = EIO;
416 goto cleanup_f;
417 }
418
419 *res = gr;
420
421 cleanup_f:
422 fclose(f);
423 goto done;
424 }
425
426 done:
427
428 #ifdef HAVE_PTHREAD_SETCANCELSTATE
429 pthread_setcancelstate(cs, 0);
430 #endif
431
432 if (rv)
433 errno = rv;
434 return rv;
435 }
436
437 static int getgr_r(const char *name, gid_t gid, struct group *gr, char *buf,
438 size_t size, struct group **res)
439 {
440 char *line = 0;
441 size_t len = 0;
442 char **mem = 0;
443 size_t nmem = 0;
444 int rv = 0;
445 size_t i;
446
447 #ifdef HAVE_PTHREAD_SETCANCELSTATE
448 int cs;
449
450 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
451 #endif
452
453 rv = __getgr_a(name, gid, gr, &line, &len, &mem, &nmem, res);
454 if (*res && size < len + (nmem + 1) * sizeof(char *) + 32) {
455 *res = 0;
456 rv = ERANGE;
457 }
458 if (*res) {
459 buf += (16 - (uintptr_t)buf) % 16;
460 gr->gr_mem = (void *)buf;
461 buf += (nmem + 1) * sizeof(char *);
462 memcpy(buf, line, len);
463 FIX(name);
464 FIX(passwd);
465 for (i = 0; mem[i]; i++)
466 gr->gr_mem[i] = mem[i] - line + buf;
467 gr->gr_mem[i] = 0;
468 }
469 free(mem);
470 free(line);
471
472 #ifdef HAVE_PTHREAD_SETCANCELSTATE
473 pthread_setcancelstate(cs, 0);
474 #endif
475
476 if (rv)
477 errno = rv;
478 return rv;
479 }
480
481 int getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t size, struct group **res)
482 {
483 return getgr_r(0, gid, gr, buf, size, res);
484 }