getopt: fix null pointer arithmetic ub
[musl] / src / passwd / getgr_a.c
1 #include <pthread.h>
2 #include <byteswap.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include "pwf.h"
6 #include "nscd.h"
7
8 static char *itoa(char *p, uint32_t x)
9 {
10         // number of digits in a uint32_t + NUL
11         p += 11;
12         *--p = 0;
13         do {
14                 *--p = '0' + x % 10;
15                 x /= 10;
16         } while (x);
17         return p;
18 }
19
20 int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t *size, char ***mem, size_t *nmem, struct group **res)
21 {
22         FILE *f;
23         int rv = 0;
24         int cs;
25
26         *res = 0;
27
28         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
29         f = fopen("/etc/group", "rbe");
30         if (!f) {
31                 rv = errno;
32                 goto done;
33         }
34
35         while (!(rv = __getgrent_a(f, gr, buf, size, mem, nmem, res)) && *res) {
36                 if (name && !strcmp(name, (*res)->gr_name)
37                 || !name && (*res)->gr_gid == gid) {
38                         break;
39                 }
40         }
41         fclose(f);
42
43         if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) {
44                 int32_t req = name ? GETGRBYNAME : GETGRBYGID;
45                 int32_t i;
46                 const char *key;
47                 int32_t groupbuf[GR_LEN] = {0};
48                 size_t len = 0;
49                 size_t grlist_len = 0;
50                 char gidbuf[11] = {0};
51                 int swap = 0;
52                 char *ptr;
53
54                 if (name) {
55                         key = name;
56                 } else {
57                         if (gid < 0 || gid > UINT32_MAX) {
58                                 rv = 0;
59                                 goto done;
60                         }
61                         key = itoa(gidbuf, gid);
62                 }
63
64                 f = __nscd_query(req, key, groupbuf, sizeof groupbuf, &swap);
65                 if (!f) { rv = errno; goto done; }
66
67                 if (!groupbuf[GRFOUND]) { rv = 0; goto cleanup_f; }
68
69                 if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) {
70                         rv = EIO;
71                         goto cleanup_f;
72                 }
73
74                 if (groupbuf[GRNAMELEN] > SIZE_MAX - groupbuf[GRPASSWDLEN]) {
75                         rv = ENOMEM;
76                         goto cleanup_f;
77                 }
78                 len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
79
80                 for (i = 0; i < groupbuf[GRMEMCNT]; i++) {
81                         uint32_t name_len;
82                         if (fread(&name_len, sizeof name_len, 1, f) < 1) {
83                                 rv = ferror(f) ? errno : EIO;
84                                 goto cleanup_f;
85                         }
86                         if (swap) {
87                                 name_len = bswap_32(name_len);
88                         }
89                         if (name_len > SIZE_MAX - grlist_len
90                         || name_len > SIZE_MAX - len) {
91                                 rv = ENOMEM;
92                                 goto cleanup_f;
93                         }
94                         len += name_len;
95                         grlist_len += name_len;
96                 }
97
98                 if (len > *size || !*buf) {
99                         char *tmp = realloc(*buf, len);
100                         if (!tmp) {
101                                 rv = errno;
102                                 goto cleanup_f;
103                         }
104                         *buf = tmp;
105                         *size = len;
106                 }
107
108                 if (!fread(*buf, len, 1, f)) {
109                         rv = ferror(f) ? errno : EIO;
110                         goto cleanup_f;
111                 }
112
113                 if (groupbuf[GRMEMCNT] + 1 > *nmem) {
114                         if (groupbuf[GRMEMCNT] + 1 > SIZE_MAX/sizeof(char*)) {
115                                 rv = ENOMEM;
116                                 goto cleanup_f;
117                         }
118                         char **tmp = realloc(*mem, (groupbuf[GRMEMCNT]+1)*sizeof(char*));
119                         if (!tmp) {
120                                 rv = errno;
121                                 goto cleanup_f;
122                         }
123                         *mem = tmp;
124                         *nmem = groupbuf[GRMEMCNT] + 1;
125                 }
126
127                 if (groupbuf[GRMEMCNT]) {
128                         mem[0][0] = *buf + groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
129                         for (ptr = mem[0][0], i = 0; ptr != mem[0][0]+grlist_len; ptr++)
130                                 if (!*ptr) mem[0][++i] = ptr+1;
131                         mem[0][i] = 0;
132
133                         if (i != groupbuf[GRMEMCNT]) {
134                                 rv = EIO;
135                                 goto cleanup_f;
136                         }
137                 } else {
138                         mem[0][0] = 0;
139                 }
140
141                 gr->gr_name = *buf;
142                 gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN];
143                 gr->gr_gid = groupbuf[GRGID];
144                 gr->gr_mem = *mem;
145
146                 if (gr->gr_passwd[-1]
147                 || gr->gr_passwd[groupbuf[GRPASSWDLEN]-1]) {
148                         rv = EIO;
149                         goto cleanup_f;
150                 }
151
152                 if (name && strcmp(name, gr->gr_name)
153                 || !name && gid != gr->gr_gid) {
154                         rv = EIO;
155                         goto cleanup_f;
156                 }
157
158                 *res = gr;
159
160 cleanup_f:
161                 fclose(f);
162                 goto done;
163         }
164
165 done:
166         pthread_setcancelstate(cs, 0);
167         if (rv) errno = rv;
168         return rv;
169 }